1 #include <QtSql>
2 #include <QString>
3 #include <QtDebug>
4 #include <QVariant>
5 #include <QThread>
6 
7 #include "libraryhashdao.h"
8 #include "library/queryutil.h"
9 
10 namespace {
11 
12 // Store hash values as a signed 64-bit integer. Otherwise values greater
13 // than 2^63-1 would be converted into a floating point numbers while
14 // losing precision!!
dbHash(mixxx::cache_key_t hash)15 inline constexpr mixxx::cache_key_signed_t dbHash(mixxx::cache_key_t hash) {
16     return mixxx::signedCacheKey(hash);
17 }
18 
19 } // anonymous namespace
20 
getDirectoryHashes()21 QHash<QString, mixxx::cache_key_t> LibraryHashDAO::getDirectoryHashes() {
22     QSqlQuery query(m_database);
23     query.prepare("SELECT hash, directory_path FROM LibraryHashes");
24     QHash<QString, mixxx::cache_key_t> hashes;
25     if (!query.exec()) {
26         LOG_FAILED_QUERY(query);
27     }
28 
29     int hashColumn = query.record().indexOf("hash");
30     int directoryPathColumn = query.record().indexOf("directory_path");
31     while (query.next()) {
32         hashes[query.value(directoryPathColumn).toString()] =
33                 query.value(hashColumn).toULongLong();
34     }
35 
36     return hashes;
37 }
38 
getDirectoryHash(const QString & dirPath)39 mixxx::cache_key_t LibraryHashDAO::getDirectoryHash(const QString& dirPath) {
40     //qDebug() << "LibraryHashDAO::getDirectoryHash" << QThread::currentThread() << m_database.connectionName();
41     mixxx::cache_key_t hash = mixxx::invalidCacheKey();
42 
43     QSqlQuery query(m_database);
44     query.prepare("SELECT hash FROM LibraryHashes "
45                   "WHERE directory_path=:directory_path");
46     query.bindValue(":directory_path", dirPath);
47 
48     if (!query.exec()) {
49         LOG_FAILED_QUERY(query) << "SELECT hash failed:";
50     }
51     //Grab a hash for this directory from the database, from the last time it was scanned.
52     if (query.next()) {
53         hash = query.value(query.record().indexOf("hash")).toULongLong();
54         //qDebug() << "prev hash exists" << hash << dirPath;
55     } else {
56         //qDebug() << "prev hash does not exist" << dirPath;
57     }
58 
59     return hash;
60 }
61 
saveDirectoryHash(const QString & dirPath,mixxx::cache_key_t hash)62 void LibraryHashDAO::saveDirectoryHash(const QString& dirPath, mixxx::cache_key_t hash) {
63     //qDebug() << "LibraryHashDAO::saveDirectoryHash" << QThread::currentThread() << m_database.connectionName();
64     QSqlQuery query(m_database);
65     query.prepare("INSERT INTO LibraryHashes (directory_path, hash, directory_deleted) "
66                     "VALUES (:directory_path, :hash, :directory_deleted)");
67     query.bindValue(":directory_path", dirPath);
68     query.bindValue(":hash", dbHash(hash));
69     query.bindValue(":directory_deleted", 0);
70 
71     if (!query.exec()) {
72         LOG_FAILED_QUERY(query) << "Creating new dirhash failed.";
73     }
74     //qDebug() << "created new hash" << hash;
75 }
76 
updateDirectoryHash(const QString & dirPath,mixxx::cache_key_t newHash,int dir_deleted)77 void LibraryHashDAO::updateDirectoryHash(const QString& dirPath,
78                                          mixxx::cache_key_t newHash,
79                                          int dir_deleted) {
80     //qDebug() << "LibraryHashDAO::updateDirectoryHash" << QThread::currentThread() << m_database.connectionName();
81     QSqlQuery query(m_database);
82     // By definition if we have calculated a new hash for a directory then it
83     // exists and no longer needs verification.
84     query.prepare("UPDATE LibraryHashes "
85             "SET hash=:hash, directory_deleted=:directory_deleted, "
86             "needs_verification=0 "
87             "WHERE directory_path=:directory_path");
88     query.bindValue(":hash", dbHash(newHash));
89     query.bindValue(":directory_deleted", dir_deleted);
90     query.bindValue(":directory_path", dirPath);
91 
92     if (!query.exec()) {
93         LOG_FAILED_QUERY(query) << "Updating existing dirhash failed.";
94     }
95     //qDebug() << "updated old existing hash" << newHash << dirPath << dir_deleted;
96 
97     //DEBUG: Print out the directory hash we just saved to verify...
98     //qDebug() << getDirectoryHash(dirPath);
99 }
100 
updateDirectoryStatuses(const QStringList & dirPaths,const bool deleted,const bool verified)101 void LibraryHashDAO::updateDirectoryStatuses(const QStringList& dirPaths,
102                                              const bool deleted,
103                                              const bool verified) {
104     //qDebug() << "LibraryHashDAO::updateDirectoryStatus" << QThread::currentThread() << m_database.connectionName();
105     FieldEscaper escaper(m_database);
106     QStringList escapedDirPaths = escaper.escapeStrings(dirPaths);
107 
108     QSqlQuery query(m_database);
109     query.prepare(
110         QString("UPDATE LibraryHashes "
111                 "SET directory_deleted=:directory_deleted, "
112                 "needs_verification=:needs_verification "
113                 "WHERE directory_path IN (%1)")
114         .arg(escapedDirPaths.join(",")));
115     query.bindValue(":directory_deleted", deleted ? 1 : 0);
116     query.bindValue(":needs_verification", !verified ? 1 : 0);
117     if (!query.exec()) {
118         LOG_FAILED_QUERY(query) << "Updating directory status failed.";
119     }
120 }
121 
markAsExisting(const QString & dirPath)122 void LibraryHashDAO::markAsExisting(const QString& dirPath) {
123     //qDebug() << "LibraryHashDAO::markExisting" << QThread::currentThread() << m_database.connectionName();
124     QSqlQuery query(m_database);
125     query.prepare("UPDATE LibraryHashes "
126                   "SET directory_deleted=:directory_deleted "
127                   "WHERE directory_path=:directory_path");
128     query.bindValue(":directory_deleted", 0);
129     query.bindValue(":directory_path", dirPath);
130     if (!query.exec()) {
131         LOG_FAILED_QUERY(query) << "Updating dirhash to mark as existing failed.";
132     }
133 }
134 
invalidateAllDirectories()135 void LibraryHashDAO::invalidateAllDirectories() {
136     //qDebug() << "LibraryHashDAO::invalidateAllDirectories"
137     //<< QThread::currentThread() << m_database.connectionName();
138     QSqlQuery query(m_database);
139     query.prepare("UPDATE LibraryHashes "
140                   "SET needs_verification=1");
141     if (!query.exec()) {
142         LOG_FAILED_QUERY(query)
143                 << "Couldn't mark directories previously hashed as needing verification.";
144     }
145 }
146 
markUnverifiedDirectoriesAsDeleted()147 void LibraryHashDAO::markUnverifiedDirectoriesAsDeleted() {
148     //qDebug() << "LibraryHashDAO::markUnverifiedDirectoriesAsDeleted"
149     //<< QThread::currentThread() << m_database.connectionName();
150     QSqlQuery query(m_database);
151     query.prepare("UPDATE LibraryHashes "
152                   "SET directory_deleted=:directory_deleted "
153                   "WHERE needs_verification=1");
154     query.bindValue(":directory_deleted", 1);
155     if (!query.exec()) {
156         LOG_FAILED_QUERY(query);
157     }
158 }
159 
removeDeletedDirectoryHashes()160 void LibraryHashDAO::removeDeletedDirectoryHashes() {
161     QSqlQuery query(m_database);
162     query.prepare("DELETE FROM LibraryHashes WHERE "
163                "directory_deleted=:directory_deleted");
164     query.bindValue(":directory_deleted", 1);
165     if (!query.exec()) {
166         LOG_FAILED_QUERY(query);
167     }
168 }
169 
getDeletedDirectories()170 QStringList LibraryHashDAO::getDeletedDirectories() {
171     QStringList result;
172     QSqlQuery query(m_database);
173     query.prepare("SELECT directory_path FROM LibraryHashes "
174                   "WHERE directory_deleted=1");
175     if (!query.exec()) {
176         LOG_FAILED_QUERY(query);
177     }
178     const int directoryPathColumn = query.record().indexOf("directory_path");
179     while (query.next()) {
180         QString directory = query.value(directoryPathColumn).toString();
181         result << directory;
182     }
183     return result;
184 }
185