/* 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 Database : public QObject { Q_OBJECT public: Database(QObject* parent = 0, const QString& database_name = QString()); static const int kSchemaVersion; static const char* kDatabaseFilename; static const char* kMagicAllSongsTables; void Stop() {} QSqlDatabase Connect(); bool CheckErrors(const QSqlQuery& query); QMutex* Mutex() { return &mutex_; } void RecreateAttachedDb(const QString& database_name); void ExecFromFile(const QString& filename, QSqlDatabase &db); void ExecCommands(const QString& commands, QSqlDatabase &db); int startup_schema_version() const { return startup_schema_version_; } int current_schema_version() const { return kSchemaVersion; } signals: void Error(const QString& message); private: void UpdateMainSchema(QSqlDatabase* db); void UpdateDatabaseSchema(int version, QSqlDatabase& db); QStringList SongsTables(QSqlDatabase& db) const; struct AttachedDatabase { AttachedDatabase() {} AttachedDatabase(const QString& filename, const QString& schema) : filename_(filename), schema_(schema) {} QString filename_; QString schema_; }; // 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, LikeWorksWithAllAscii); FRIEND_TEST(DatabaseTest, LikeWorksWithUnicode); FRIEND_TEST(DatabaseTest, LikeAsciiCaseInsensitive); FRIEND_TEST(DatabaseTest, LikeUnicodeCaseInsensitive); FRIEND_TEST(DatabaseTest, LikePerformance); FRIEND_TEST(DatabaseTest, LikeCacheInvalidated); FRIEND_TEST(DatabaseTest, LikeQuerySplit); FRIEND_TEST(DatabaseTest, LikeDecomposes); 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(); // Custom LIKE() function for sqlite. bool Like(const char* needle, const char* haystack); static void SqliteLike(sqlite3_context* context, int argc, sqlite3_value** argv); typedef int (*Sqlite3CreateFunc) ( sqlite3*, const char*, int, int, void*, void (*) (sqlite3_context*, int, sqlite3_value**), void (*) (sqlite3_context*, int, sqlite3_value**), void (*) (sqlite3_context*)); // Sqlite3 functions. These will be loaded from the sqlite3 plugin. static Sqlite3CreateFunc _sqlite3_create_function; static int (*_sqlite3_value_type) (sqlite3_value*); static sqlite_int64 (*_sqlite3_value_int64) (sqlite3_value*); static const uchar* (*_sqlite3_value_text) (sqlite3_value*); static void (*_sqlite3_result_int64) (sqlite3_context*, sqlite_int64); static void* (*_sqlite3_user_data) (sqlite3_context*); static bool sStaticInitDone; static bool sLoadedSqliteSymbols; 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(QObject* parent = 0) : Database(parent, ":memory:") {} ~MemoryDatabase() { // Make sure Qt doesn't reuse the same database QSqlDatabase::removeDatabase(Connect().connectionName()); } }; #endif // DATABASE_H