diff options
-rw-r--r-- | build/depends.py | 1 | ||||
-rw-r--r-- | src/library/banshee/bansheeplaylistmodel.cpp | 4 | ||||
-rw-r--r-- | src/library/basesqltablemodel.cpp | 37 | ||||
-rw-r--r-- | src/library/basetrackcache.cpp | 4 | ||||
-rw-r--r-- | src/library/browse/browsetablemodel.cpp | 2 | ||||
-rw-r--r-- | src/library/crate/cratestorage.cpp | 21 | ||||
-rw-r--r-- | src/library/itunes/itunesfeature.cpp | 4 | ||||
-rw-r--r-- | src/library/librarytablemodel.cpp | 2 | ||||
-rw-r--r-- | src/library/mixxxlibraryfeature.cpp | 2 | ||||
-rw-r--r-- | src/library/playlistfeature.cpp | 4 | ||||
-rw-r--r-- | src/library/rhythmbox/rhythmboxfeature.cpp | 2 | ||||
-rw-r--r-- | src/library/scanner/libraryscanner.cpp | 2 | ||||
-rw-r--r-- | src/library/setlogfeature.cpp | 2 | ||||
-rw-r--r-- | src/library/trackcollection.cpp | 398 | ||||
-rw-r--r-- | src/library/trackcollection.h | 30 | ||||
-rw-r--r-- | src/library/traktor/traktorfeature.cpp | 2 | ||||
-rw-r--r-- | src/test/sqliteliketest.cpp | 34 | ||||
-rw-r--r-- | src/util/db/dbconnection.cpp | 418 | ||||
-rw-r--r-- | src/util/db/dbconnection.h | 40 | ||||
-rw-r--r-- | src/util/string.h | 16 |
20 files changed, 548 insertions, 477 deletions
diff --git a/build/depends.py b/build/depends.py index 79d328c55a..b416902679 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1099,6 +1099,7 @@ class MixxxCore(Feature): "util/tapfilter.cpp", "util/movinginterquartilemean.cpp", "util/console.cpp", + "util/db/dbconnection.cpp", "util/db/dbid.cpp", "util/db/fwdsqlquery.cpp", "util/db/sqllikeescaper.cpp", diff --git a/src/library/banshee/bansheeplaylistmodel.cpp b/src/library/banshee/bansheeplaylistmodel.cpp index 5bfcfb977b..c871095bd3 100644 --- a/src/library/banshee/bansheeplaylistmodel.cpp +++ b/src/library/banshee/bansheeplaylistmodel.cpp @@ -50,7 +50,7 @@ void BansheePlaylistModel::setTableModel(int playlistId) { if (m_playlistId >= 0) { // Clear old playlist m_playlistId = -1; - QSqlQuery query(m_pTrackCollection->getDatabase()); + QSqlQuery query(m_pTrackCollection->database()); QString strQuery("DELETE FROM " BANSHEE_TABLE); if (!query.exec(strQuery)) { LOG_FAILED_QUERY(query); @@ -61,7 +61,7 @@ void BansheePlaylistModel::setTableModel(int playlistId) { // setup new playlist m_playlistId = playlistId; - QSqlQuery query(m_pTrackCollection->getDatabase()); + QSqlQuery query(m_pTrackCollection->database()); QString strQuery("CREATE TEMP TABLE IF NOT EXISTS " BANSHEE_TABLE " (" CLM_VIEW_ORDER " INTEGER, " CLM_ARTIST " TEXT, " diff --git a/src/library/basesqltablemodel.cpp b/src/library/basesqltablemodel.cpp index 56fe2efa94..b734e1d151 100644 --- a/src/library/basesqltablemodel.cpp +++ b/src/library/basesqltablemodel.cpp @@ -29,16 +29,16 @@ static const bool sDebug = false; static const int kIdColumn = 0; static const int kMaxSortColumns = 3; -// Constant for getModelSetting(name) +// Constant for getModelSetting(name) static const char* COLUMNS_SORTING = "ColumnsSorting"; BaseSqlTableModel::BaseSqlTableModel(QObject* pParent, TrackCollection* pTrackCollection, const char* settingsNamespace) : QAbstractTableModel(pParent), - TrackModel(pTrackCollection->getDatabase(), settingsNamespace), + TrackModel(pTrackCollection->database(), settingsNamespace), m_pTrackCollection(pTrackCollection), - m_database(pTrackCollection->getDatabase()), + m_database(pTrackCollection->database()), m_previewDeckGroup(PlayerManager::groupForPreviewDeck(0)), m_bInitialized(false), m_currentSearch("") { @@ -424,24 +424,24 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { qWarning() << "BaseSqlTableModel::setSort invalid column:" << column; return; } - + // There's no item to sort already, load from Settings last sort if (m_sortColumns.isEmpty()) { QString val = getModelSetting(COLUMNS_SORTING); QTextStream in(&val); - + while (!in.atEnd()) { int ordI = -1; QString name; - + in >> name >> ordI; - + int col = fieldIndex(name); if (col < 0) continue; - + Qt::SortOrder ord; ord = ordI > 0 ? Qt::AscendingOrder : Qt::DescendingOrder; - + m_sortColumns << SortColumn(col, ord); } } @@ -465,13 +465,13 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { m_sortColumns.removeLast(); } } - + // Write new sortColumns order to user settings QString val; QTextStream out(&val); for (SortColumn& sc : m_sortColumns) { - QString name; + QString name; if (sc.m_column > 0 && sc.m_column < m_tableColumns.size()) { name = m_tableColumns[sc.m_column]; } else { @@ -489,7 +489,7 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { if (sDebug) { qDebug() << "setSort() sortColumns:" << val; } - + // we have two selects for sorting, since keeping the select history // across the two selects is hard, we do this only for the trackSource @@ -511,9 +511,8 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { QString field = m_tableColumns[column]; QString sort_field = QString("%1.%2").arg(m_tableName, field); m_tableOrderBy.append(sort_field); - #ifdef __SQLITE3__ - m_tableOrderBy.append(" COLLATE localeAwareCompare"); - #endif + m_tableOrderBy.append(" COLLATE "); + m_tableOrderBy.append(DbConnection::kStringCollationFunc); m_tableOrderBy.append((order == Qt::AscendingOrder) ? " ASC" : " DESC"); } m_sortColumns.clear(); @@ -530,7 +529,7 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { sort_field = "RANDOM()"; } else { // we can't sort by other table columns here since primary sort is a track - // column: skip + // column: skip continue; } } else { @@ -544,10 +543,8 @@ void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { m_trackSourceOrderBy.append(first ? "ORDER BY ": ", "); m_trackSourceOrderBy.append(sort_field); - - #ifdef __SQLITE3__ - m_trackSourceOrderBy.append(" COLLATE localeAwareCompare"); - #endif + m_trackSourceOrderBy.append(" COLLATE "); + m_trackSourceOrderBy.append(DbConnection::kStringCollationFunc); m_trackSourceOrderBy.append((sc.m_order == Qt::AscendingOrder) ? " ASC" : " DESC"); //qDebug() << m_trackSourceOrderBy; diff --git a/src/library/basetrackcache.cpp b/src/library/basetrackcache.cpp index 882945d7a1..a1f4b96346 100644 --- a/src/library/basetrackcache.cpp +++ b/src/library/basetrackcache.cpp @@ -32,8 +32,8 @@ BaseTrackCache::BaseTrackCache(TrackCollection* pTrackCollection, m_bIndexBuilt(false), m_bIsCaching(isCaching), m_trackDAO(pTrackCollection->getTrackDAO()), - m_database(pTrackCollection->getDatabase()), - m_pQueryParser(new SearchQueryParser(pTrackCollection->getDatabase())) { + m_database(pTrackCollection->database()), + m_pQueryParser(new SearchQueryParser(pTrackCollection->database())) { m_searchColumns << "artist" << "album" << "album_artist" diff --git a/src/library/browse/browsetablemodel.cpp b/src/library/browse/browsetablemodel.cpp index 93f3ae3ff6..a1dc266530 100644 --- a/src/library/browse/browsetablemodel.cpp +++ b/src/library/browse/browsetablemodel.cpp @@ -17,7 +17,7 @@ BrowseTableModel::BrowseTableModel(QObject* parent, TrackCollection* pTrackCollection, RecordingManager* pRecordingManager) - : TrackModel(pTrackCollection->getDatabase(), + : TrackModel(pTrackCollection->database(), "mixxx.db.model.browse"), QStandardItemModel(parent), m_pTrackCollection(pTrackCollection), diff --git a/src/library/crate/cratestorage.cpp b/src/library/crate/cratestorage.cpp index 3176a1b633..83de31e151 100644 --- a/src/library/crate/cratestorage.cpp +++ b/src/library/crate/cratestorage.cpp @@ -1,10 +1,11 @@ #include "library/crate/cratestorage.h" #include "library/crate/crateschema.h" - #include "library/dao/trackschema.h" -#include "util/db/fwdsqlquery.h" + +#include "util/db/dbconnection.h" #include "util/db/sqltransaction.h" +#include "util/db/fwdsqlquery.h" #include <QtDebug> @@ -318,9 +319,11 @@ bool CrateStorage::readCrateByName(const QString& name, Crate* pCrate) const { CrateSelectIterator CrateStorage::selectCrates() const { FwdSqlQuery query(m_database, QString( - "SELECT * FROM %1 ORDER BY %2 COLLATE localeAwareCompare").arg( + "SELECT * FROM %1 ORDER BY %2 COLLATE %3").arg( CRATE_TABLE, - CRATETABLE_NAME)); + CRATETABLE_NAME, + DbConnection::kStringCollationFunc)); + if (query.execPrepared()) { return CrateSelectIterator(query); } else { @@ -331,10 +334,11 @@ CrateSelectIterator CrateStorage::selectCrates() const { CrateSelectIterator CrateStorage::selectAutoDjCrates(bool autoDjSource) const { FwdSqlQuery query(m_database, QString( - "SELECT * FROM %1 WHERE %2=:autoDjSource ORDER BY %3 COLLATE localeAwareCompare").arg( + "SELECT * FROM %1 WHERE %2=:autoDjSource ORDER BY %3 COLLATE %4").arg( CRATE_TABLE, CRATETABLE_AUTODJ_SOURCE, - CRATETABLE_NAME)); + CRATETABLE_NAME, + DbConnection::kStringCollationFunc)); query.bindValue(":autoDjSource", autoDjSource); if (query.execPrepared()) { return CrateSelectIterator(query); @@ -346,9 +350,10 @@ CrateSelectIterator CrateStorage::selectAutoDjCrates(bool autoDjSource) const { CrateSummarySelectIterator CrateStorage::selectCrateSummaries() const { FwdSqlQuery query(m_database, QString( - "SELECT * FROM %1 ORDER BY %2 COLLATE localeAwareCompare").arg( + "SELECT * FROM %1 ORDER BY %2 COLLATE %3").arg( CRATE_SUMMARY_VIEW, - CRATETABLE_NAME)); + CRATETABLE_NAME, + DbConnection::kStringCollationFunc)); if (query.execPrepared()) { return CrateSummarySelectIterator(query); } else { diff --git a/src/library/itunes/itunesfeature.cpp b/src/library/itunes/itunesfeature.cpp index 5a3a4d1a80..fa13586498 100644 --- a/src/library/itunes/itunesfeature.cpp +++ b/src/library/itunes/itunesfeature.cpp @@ -68,7 +68,7 @@ ITunesFeature::ITunesFeature(QObject* parent, TrackCollection* pTrackCollection) m_isActivated = false; m_title = tr("iTunes"); - m_database = QSqlDatabase::cloneDatabase(pTrackCollection->getDatabase(), "ITUNES_SCANNER"); + m_database = QSqlDatabase::cloneDatabase(pTrackCollection->database(), "ITUNES_SCANNER"); //Open the database connection in this thread. if (!m_database.open()) { @@ -120,7 +120,7 @@ void ITunesFeature::activate() { void ITunesFeature::activate(bool forceReload) { //qDebug("ITunesFeature::activate()"); if (!m_isActivated || forceReload) { - SettingsDAO settings(m_pTrackCollection->getDatabase()); + SettingsDAO settings(m_pTrackCollection->database()); QString dbSetting(settings.getValue(ITDB_PATH_KEY)); // if a path exists in the database, use it if (!dbSetting.isEmpty() && QFile::exists(dbSetting)) { diff --git a/src/library/librarytablemodel.cpp b/src/library/librarytablemodel.cpp index 21ea1e8676..c6c2f504ea 100644 --- a/src/library/librarytablemodel.cpp +++ b/src/library/librarytablemodel.cpp @@ -32,7 +32,7 @@ void LibraryTableModel::setTableModel(int id) { const QString tableName = "library_view"; - QSqlQuery query(m_pTrackCollection->getDatabase()); + QSqlQuery query(m_pTrackCollection->database()); QString queryString = "CREATE TEMPORARY VIEW IF NOT EXISTS " + tableName + " AS " "SELECT " + columns.join(", ") + " FROM library INNER JOIN track_locations " diff --git a/src/library/mixxxlibraryfeature.cpp b/src/library/mixxxlibraryfeature.cpp index 43393d1d9b..bb090dba9c 100644 --- a/src/library/mixxxlibraryfeature.cpp +++ b/src/library/mixxxlibraryfeature.cpp @@ -66,7 +66,7 @@ MixxxLibraryFeature::MixxxLibraryFeature(Library* pLibrary, << "library." + LIBRARYTABLE_COVERART_LOCATION << "library." + LIBRARYTABLE_COVERART_HASH; - QSqlQuery query(pTrackCollection->getDatabase()); + QSqlQuery query(pTrackCollection->database()); QString tableName = "library_cache_view"; QString queryString = QString( "CREATE TEMPORARY VIEW IF NOT EXISTS %1 AS " diff --git a/src/library/playlistfeature.cpp b/src/library/playlistfeature.cpp index 2ea3ef6348..0f1d513d08 100644 --- a/src/library/playlistfeature.cpp +++ b/src/library/playlistfeature.cpp @@ -140,13 +140,13 @@ void PlaylistFeature::buildPlaylistList() { "LEFT JOIN library ON PlaylistTracks.track_id = library.id " "WHERE Playlists.hidden = 0 " "GROUP BY Playlists.id;"); - QSqlQuery query(m_pTrackCollection->getDatabase()); + QSqlQuery query(m_pTrackCollection->database()); if (!query.exec(queryString)) { LOG_FAILED_QUERY(query); } // Setup the sidebar playlist model - QSqlTableModel playlistTableModel(this, m_pTrackCollection->getDatabase()); + QSqlTableModel playlistTableModel(this, m_pTrackCollection->database()); playlistTableModel.setTable("PlaylistsCountsDurations"); playlistTableModel.setSort(playlistTableModel.fieldIndex("sort_name"), Qt::AscendingOrder); diff --git a/src/library/rhythmbox/rhythmboxfeature.cpp b/src/library/rhythmbox/rhythmboxfeature.cpp index 81dbc2029e..47a4e001e6 100644 --- a/src/library/rhythmbox/rhythmboxfeature.cpp +++ b/src/library/rhythmbox/rhythmboxfeature.cpp @@ -57,7 +57,7 @@ RhythmboxFeature::RhythmboxFeature(QObject* parent, TrackCollection* pTrackColle m_isActivated = false; m_title = tr("Rhythmbox"); - m_database = QSqlDatabase::cloneDatabase(pTrackCollection->getDatabase(), + m_database = QSqlDatabase::cloneDatabase(pTrackCollection->database(), "RHYTHMBOX_SCANNER"); //Open the database connection in this thread. diff --git a/src/library/scanner/libraryscanner.cpp b/src/library/scanner/libraryscanner.cpp index e61fc2479a..843eb31d05 100644 --- a/src/library/scanner/libraryscanner.cpp +++ b/src/library/scanner/libraryscanner.cpp @@ -124,7 +124,7 @@ void LibraryScanner::run() { Trace trace("LibraryScanner"); if (m_pCollection != NULL) { // false only during tests if (!m_database.isValid()) { - m_database = QSqlDatabase::cloneDatabase(m_pCollection->getDatabase(), "LIBRARY_SCANNER"); + m_database = QSqlDatabase::cloneDatabase(m_pCollection->database(), "LIBRARY_SCANNER"); } if (!m_database.isOpen()) { diff --git a/src/library/setlogfeature.cpp b/src/library/setlogfeature.cpp index 18dbbdef4f..530e157c21 100644 --- a/src/library/setlogfeature.cpp +++ b/src/library/setlogfeature.cpp @@ -117,7 +117,7 @@ void SetlogFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index void SetlogFeature::buildPlaylistList() { m_playlistList.clear(); // Setup the sidebar playlist model - QSqlTableModel playlistTableModel(this, m_pTrackCollection->getDatabase()); + QSqlTableModel playlistTableModel(this, m_pTrackCollection->database()); playlistTableModel.setTable("Playlists"); playlistTableModel.setFilter("hidden=2"); // PLHT_SET_LOG playlistTableModel.setSort(playlistTableModel.fieldIndex("id"), diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index 70a1bcd46c..6c8559f7b5 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -3,10 +3,6 @@ #include "library/trackcollection.h" -#ifdef __SQLITE3__ -#include <sqlite3.h> -#endif - #include "library/librarytablemodel.h" #include "library/schemamanager.h" #include "library/crate/cratestorage.h" @@ -20,25 +16,14 @@ const int TrackCollection::kRequiredSchemaVersion = 27; TrackCollection::TrackCollection(UserSettingsPointer pConfig) : m_pConfig(pConfig), - m_db(QSqlDatabase::addDatabase("QSQLITE")), // defaultConnection - m_playlistDao(m_db), - m_cueDao(m_db), - m_directoryDao(m_db), - m_analysisDao(m_db, pConfig), - m_libraryHashDao(m_db), - m_trackDao(m_db, m_cueDao, m_playlistDao, + m_dbConnection(m_pConfig->getSettingsPath()), + m_playlistDao(database()), + m_cueDao(database()), + m_directoryDao(database()), + m_analysisDao(database(), pConfig), + m_libraryHashDao(database()), + m_trackDao(database(), m_cueDao, m_playlistDao, m_analysisDao, m_libraryHashDao, pConfig) { - qDebug() << "Available QtSQL drivers:" << QSqlDatabase::drivers(); - - m_db.setHostName("localhost"); - m_db.setDatabaseName(QDir(pConfig->getSettingsPath()).filePath("mixxxdb.sqlite")); - m_db.setUserName("mixxx"); - m_db.setPassword("mixxx"); - bool ok = m_db.open(); - qDebug() << "DB status:" << m_db.databaseName() << "=" << ok; - if (m_db.lastError().isValid()) { - qDebug() << "Error loading database:" << m_db.lastError(); - } // Check for tables and create them if missing if (!checkForTables()) { // TODO(XXX) something a little more elegant @@ -50,24 +35,10 @@ TrackCollection::~TrackCollection() { qDebug() << "~TrackCollection()"; m_trackDao.finish(); m_crates.detachDatabase(); - - if (m_db.isOpen()) { - // There should never be an outstanding transaction when this code is - // called. If there is, it means we probably aren't committing a - // transaction somewhere that should be. - if (m_db.rollback()) { - qDebug() << "ERROR: There was a transaction in progress on the main database connection while shutting down." - << "There is a logic error somewhere."; - } - m_db.close(); - } else { - qDebug() << "ERROR: The main database connection was closed before TrackCollection closed it." - << "There is a logic error somewhere."; - } } bool TrackCollection::checkForTables() { - if (!m_db.open()) { + if (!m_dbConnection) { QMessageBox::critical(0, tr("Cannot open database"), tr("Unable to establish a database connection.\n" "Mixxx requires QT with SQLite support. Please read " @@ -77,10 +48,6 @@ bool TrackCollection::checkForTables() { return false; } -#ifdef __SQLITE3__ - installSorting(m_db); -#endif - // The schema XML is baked into the binary via Qt resources. QString schemaFilename(":/schema.xml"); QString okToExit = tr("Click OK to exit."); @@ -92,7 +59,7 @@ bool TrackCollection::checkForTables() { "mixxx-devel@lists.sourceforge.net"; SchemaManager::Result result = SchemaManager::upgradeToSchemaVersion( - schemaFilename, m_db, kRequiredSchemaVersion); + schemaFilename, database(), kRequiredSchemaVersion); switch (result) { case SchemaManager::RESULT_BACKWARDS_INCOMPATIBLE: QMessageBox::warning( @@ -130,14 +97,10 @@ bool TrackCollection::checkForTables() { m_cueDao.initialize(); m_directoryDao.initialize(); m_libraryHashDao.initialize(); - m_crates.attachDatabase(m_db); + m_crates.attachDatabase(database()); return true; } -QSqlDatabase& TrackCollection::getDatabase() { - return m_db; -} - TrackDAO& TrackCollection::getTrackDAO() { return m_trackDao; } @@ -391,344 +354,3 @@ bool TrackCollection::removeCrateTracks( return true; } - -#ifdef __SQLITE3__ - -// from public domain code -// http://www.archivum.info/qt-interest@trolltech.com/2008-12/00584/Re-%28Qt-interest%29-Qt-Sqlite-UserDefinedFunction.html - -namespace { - -inline -int compareLocalAwareCaseInsensitive( - const QString& first, const QString& second) { - return QString::localeAwareCompare(first.toLower(), second.toLower()); -} - -void makeLatinLow(QChar* c, int count) { - for (int i = 0; i < count; ++i) { - if (c[i].decompositionTag() != QChar::NoDecomposition) { - c[i] = c[i].decomposition()[0]; - } - if (c[i].isUpper()) { - c[i] = c[i].toLower(); - } - } -} - -const QChar LIKE_MATCH_ONE = '_'; -const QChar LIKE_MATCH_ALL = '%'; -const QChar LIKE_DEFAULT_ESCAPE = '\0'; - -// The collating function callback is invoked with a copy of the pArg -// application data pointer and with two strings in the encoding specified -// by the eTextRep argument. -// The collating function must return an integer that is negative, zero, -// or positive if the first string is less than, equal to, or greater -// than the second, respectively. -int sqliteStringCompare(void* pArg, - int len1, const void* data1, - int len2, const void* data2) { - Q_UNUSED(pArg); - // Construct a QString without copy - QString string1 = QString::fromRawData(reinterpret_cast<const QChar*>(data1), - len1 / sizeof(QChar)); - QString string2 = QString::fromRawData(reinterpret_cast<const QChar*>(data2), - len2 / sizeof(QChar)); - return compareLocalAwareCaseInsensitive(string1, string2); -} - -// Compare two strings for equality where the first string is -// a "LIKE" expression. Return true (1) if they are the same and -// false (0) if they are different. -// This is the original sqlite3 icuLikeCompare rewritten for QChar -int likeCompareInner( - const QChar* pattern, // LIKE pattern - int patternSize, - const QChar* string, // The string to compare against - int stringSize, - const QChar esc) { // The escape character - - int iPattern = 0; // Current index in pattern - int iString = 0; // Current index in string - - bool prevEscape = false; // True if the previous character was uEsc - - while (iPattern < patternSize) { - // Read (and consume) the next character from the input pattern. - QChar uPattern = pattern[iPattern++]; - // There are now 4 possibilities: - // 1. uPattern is an unescaped match-all character "%", - // 2. uPattern is an unescaped match-one character "_", - // 3. uPattern is an unescaped escape character, or - // 4. uPattern is to be handled as an ordinary character - - if (!prevEscape && uPattern == LIKE_MATCH_ALL) { - // Case 1. - QChar c; - - // Skip any LIKE_MATCH_ALL or LIKE_MATCH_ONE characters that follow a - // LIKE_MATCH_ALL. For each LIKE_MATCH_ONE, skip one character in the - // test string. - - if (iPattern >= patternSize) { - // Tailing % - return 1; - } - - while ((c = pattern[iPattern]) == LIKE_MATCH_ALL || c == LIKE_MATCH_ONE) { - if (c == LIKE_MATCH_ONE) { - if (++iString == stringSize) { - return 0; - } - } - if (++iPattern == patternSize) { - // Two or more tailing % - return 1; - } - } - - while (iString < stringSize) { - if (likeCompareInner(&pattern[iPattern], patternSize - iPattern, - &string[iString], stringSize - iString, esc)) { - return 1; - } - iString++; - } - return 0; - } else if (!prevEscape && uPattern == LIKE_MATCH_ONE) { - // Case 2. - if (++iString == stringSize) { - return 0; - } - } else if (!prevEscape && uPattern == esc) { - // Case 3. - prevEscape = 1; - } else { - // Case 4. - if (iString == stringSize) { - return 0; - } - QChar uString = string[iString++]; - if (uString != uPattern) { - return 0; - } - prevEscape = false; - } - } - return iString == stringSize; -} - -} // anonymous namespace - -void TrackCollection::installSorting(QSqlDatabase &db) { - QVariant v = db.driver()->handle(); - if (v.isValid() && strcmp(v.typeName(), "sqlite3*") == 0) { - // v.data() returns a pointer to the handle - sqlite3* handle = *static_cast<sqlite3**>(v.data()); - if (handle != 0) { // check that it is not NULL - int result = sqlite3_create_collation( - handle, - "localeAwareCompare", - SQLITE_UTF16, - NULL, - sqliteStringCompare); - if (result != SQLITE_OK) - qWarning() << "Could not add string collation function: " << result; - - result = sqlite3_create_function( - handle, - "like", - 2, - SQLITE_ANY, - NULL, - sqliteLike, - NULL, NULL); - if (result != SQLITE_OK) - qWarning() << "Could not add like 2 function: " << result; - - result = sqlite3_create_function( - handle, - "like", - 3, - SQLITE_UTF8, // No conversion, Data is stored as UTF8 - NULL, - sqliteLike, - NULL, NULL); - if (result != SQLITE_OK) - qWarning() << "Could not add like 3 function: " << result; - } else { - qWarning() << "Could not get sqlite handle"; - } - } else { - qWarning() << "handle variant returned typename " << v.typeName(); - } -} - -// This implements the like() SQL function. This is used by the LIKE operator. -// The SQL statement 'A LIKE B' is implemented as 'like(B, A)', and if there is -// an escape character, say E, it is implemented as 'like(B, A, E)' -//static -void TrackCollection::sqliteLike(sqlite3_context *context, - int aArgc, - sqlite3_value **aArgv) { - VERIFY_OR_DEBUG_ASSERT(aArgc == 2 || aArgc == 3) { - return; - } - - const char* b = reinterpret_cast<const char*>( - sqlite3_value_text(aArgv[0])); - const char* a = reinterpret_cast<const char*>( - sqlite3_value_text(aArgv[1])); - - if (!a || !b) { - return; - } - - QString stringB = QString::fromUtf8(b); // Like String - QString stringA = QString::fromUtf8(a); - - QChar esc = LIKE_DEFAULT_ESCAPE; - if (aArgc == 3) { - const char* e = reinterpret_cast<const char*>( - sqlite3_value_text(aArgv[2])); - if (e) { - QString stringE = QString::fromUtf8(e); - if (!stringE.isEmpty()) { - esc = stringE.data()[0]; - } - } - } - - int ret = likeCompareLatinLow(&stringB, &stringA, esc); - sqlite3_result_int64(context, ret); - return; -} - -//static -int TrackCollection::likeCompareLatinLow( - QString* pattern, - QString* string, - QChar esc) { - makeLatinLow(pattern->data(), pattern->length()); - makeLatinLow(string->data(), string->length()); - //qDebug() << *pattern << *string; - return likeCompareInner(pattern->data(), pattern->length(), string->data(), string->length(), esc); -} - - -/* -static int -likeCompare(nsAString::const_iterator aPatternItr, - nsAString::const_iterator aPatternEnd, - nsAString::const_iterator aStringItr, - nsAString::const_iterator aStringEnd, - PRUnichar aEscape) -{ - const PRUnichar LIKE_MATCH_ALL('%'); - const PRUnichar LIKE_MATCH_ONE('_'); - - PRBool lastWasEscape = PR_FALSE; - while (aPatternItr != aPatternEnd) { - -* What we do in here is take a look at each character from the input -* pattern, and do something with it. There are 4 possibilities: -* 1) character is an un-escaped match-all character -* 2) character is an un-escaped match-one character -* 3) character is an un-escaped escape character -* 4) character is not any of the above - - if (!lastWasEscape && *aPatternItr == LIKE_MATCH_ALL) { - // CASE 1 - -* Now we need to skip any LIKE_MATCH_ALL or LIKE_MATCH_ONE characters that follow a -* LIKE_MATCH_ALL character. For each LIKE_MATCH_ONE character, skip one character -* in the pattern string. - - while (*aPatternItr == LIKE_MATCH_ALL || *aPatternItr == LIKE_MATCH_ONE) { - if (*aPatternItr == LIKE_MATCH_ONE) { - // If we've hit the end of the string we are testing, no match - if (aStringItr == aStringEnd) - return 0; - aStringItr++; - } - aPatternItr++; - } - - // If we've hit the end of the pattern string, match - if (aPatternItr == aPatternEnd) - return 1; - - while (aStringItr != aStringEnd) { - if (likeCompare(aPatternItr, aPatternEnd, aStringItr, aStringEnd, aEscape)) { - // we've hit a match, so indicate this - return 1; - } - aStringItr++; - } - - // No match - return 0; - } else if (!lastWasEscape && *aPatternItr == LIKE_MATCH_ONE) { - // CASE 2 - if (aStringItr == aStringEnd) { - // If we've hit the end of the string we are testing, no match - return 0; - } - aStringItr++; - lastWasEscape = PR_FALSE; - } else if (!lastWasEscape && *aPatternItr == aEscape) { - // CASE 3 - lastWasEscape = PR_TRUE; - } else { - // CASE 4 - if (ToUpperCase(*aStringItr) != ToUpperCase(*aPatternItr)) { - // If we've hit a point where the strings don't match, there is no match - return 0; - } - aStringItr++; - lastWasEscape = PR_FALSE; - } - - aPatternItr++; - } - - return aStringItr == aStringEnd; -} - - -. -void -StorageUnicodeFunctions::likeFunction(sqlite3_context *p, - int aArgc, - sqlite3_value **aArgv) -{ - NS_ASSERTION(2 == aArgc || 3 == aArgc, "Invalid number of arguments!"); - - if (sqlite3_value_bytes(aArgv[0]) > SQLITE_MAX_LIKE_PATTERN_LENGTH) { - sqlite3_result_error(p, "LIKE or GLOB pattern too complex", SQLITE_TOOBIG); - return; - } - - if (!sqlite3_value_text16(aArgv[0]) || !sqlite3_value_text16(aArgv[1])) - return; - - nsDependentString A(static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[1]))); - nsDependentString B(static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[0]))); - NS_ASSERTION(!B.IsEmpty(), "LIKE string must not be null!"); - - PRUnichar E = 0; - if (3 == aArgc) - E = static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[2]))[0]; - - nsAString::const_iterator itrString, endString; - A.BeginReading(itrString); - A.EndReading(endString); |