/* This file is part of Clementine. Copyright 2010, David Sansome Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Clementine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Clementine. If not, see . */ #ifndef DATABASE_H #define DATABASE_H #include #include #include #include #include #include #include #include "gtest/gtest_prod.h" extern "C" { struct sqlite3_tokenizer; struct sqlite3_tokenizer_cursor; struct sqlite3_tokenizer_module; } class Application; class Database : public QObject { Q_OBJECT public: Database(Application* app, QObject* parent = nullptr, const QString& database_name = QString()); struct AttachedDatabase { AttachedDatabase() {} AttachedDatabase(const QString& filename, const QString& schema, bool is_temporary) : filename_(filename), schema_(schema), is_temporary_(is_temporary) {} QString filename_; QString schema_; bool is_temporary_; }; static const int kSchemaVersion; static const char* kDatabaseFilename; static const char* kMagicAllSongsTables; QSqlDatabase Connect(); bool CheckErrors(const QSqlQuery& query); QMutex* Mutex() { return &mutex_; } void RecreateAttachedDb(const QString& database_name); void ExecSchemaCommands(QSqlDatabase& db, const QString& schema, int schema_version, bool in_transaction = false); int startup_schema_version() const { return startup_schema_version_; } int current_schema_version() const { return kSchemaVersion; } void AttachDatabase(const QString& database_name, const AttachedDatabase& database); void AttachDatabaseOnDbConnection(const QString& database_name, const AttachedDatabase& database, QSqlDatabase& db); void DetachDatabase(const QString& database_name); signals: void Error(const QString& message); public slots: void DoBackup(); private: void UpdateMainSchema(QSqlDatabase* db); void ExecSchemaCommandsFromFile(QSqlDatabase& db, const QString& filename, int schema_version, bool in_transaction = false); void ExecSongTablesCommands(QSqlDatabase& db, const QStringList& song_tables, const QStringList& commands); void UpdateDatabaseSchema(int version, QSqlDatabase& db); void UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db); QStringList SongsTables(QSqlDatabase& db, int schema_version) const; bool IntegrityCheck(QSqlDatabase db); void BackupFile(const QString& filename); bool OpenDatabase(const QString& filename, sqlite3** connection) const; Application* app_; // Alias -> filename QMap attached_databases_; QString directory_; QMutex connect_mutex_; QMutex mutex_; // This ID makes the QSqlDatabase name unique to the object as well as the // thread int connection_id_; static QMutex sNextConnectionIdMutex; static int sNextConnectionId; // Used by tests QString injected_database_name_; uint query_hash_; QStringList query_cache_; // This is the schema version of Clementine's DB from the app's last run. int startup_schema_version_; FRIEND_TEST(DatabaseTest, FTSOpenParsesSimpleInput); FRIEND_TEST(DatabaseTest, FTSOpenParsesUTF8Input); FRIEND_TEST(DatabaseTest, FTSOpenParsesMultipleTokens); FRIEND_TEST(DatabaseTest, FTSCursorWorks); FRIEND_TEST(DatabaseTest, FTSOpenLeavesCyrillicQueries); // Do static initialisation like loading sqlite functions. static void StaticInit(); typedef int (*Sqlite3CreateFunc)(sqlite3*, const char*, int, int, void*, void (*)(sqlite3_context*, int, sqlite3_value**), void (*)(sqlite3_context*, int, sqlite3_value**), void (*)(sqlite3_context*)); static sqlite3_tokenizer_module* sFTSTokenizer; static int FTSCreate(int argc, const char* const* argv, sqlite3_tokenizer** tokenizer); static int FTSDestroy(sqlite3_tokenizer* tokenizer); static int FTSOpen(sqlite3_tokenizer* tokenizer, const char* input, int bytes, sqlite3_tokenizer_cursor** cursor); static int FTSClose(sqlite3_tokenizer_cursor* cursor); static int FTSNext(sqlite3_tokenizer_cursor* cursor, const char** token, int* bytes, int* start_offset, int* end_offset, int* position); struct Token { Token(const QString& token, int start, int end); QString token; int start_offset; int end_offset; }; // Based on sqlite3_tokenizer. struct UnicodeTokenizer { const sqlite3_tokenizer_module* pModule; }; struct UnicodeTokenizerCursor { const sqlite3_tokenizer* pTokenizer; QList tokens; int position; QByteArray current_utf8; }; }; class MemoryDatabase : public Database { public: MemoryDatabase(Application* app, QObject* parent = nullptr) : Database(app, parent, ":memory:") {} ~MemoryDatabase() { // Make sure Qt doesn't reuse the same database QSqlDatabase::removeDatabase(Connect().connectionName()); } }; #endif // DATABASE_H