From 5916bc11b20401a43673bb92e12f2feeb9684744 Mon Sep 17 00:00:00 2001 From: casey Date: Mon, 4 Jul 2016 15:23:13 -0700 Subject: [PATCH] - Removed CachedStatement -- it's a good idea, but implementation is a bit faulty. - Added track indexing parallelization to Indexer -- read up to 8 files at a time (value to be tweaked). Results in much faster indexing for large collections. --- src/core/CMakeLists.txt | 1 - src/core/core.vcxproj | 2 - src/core/core.vcxproj.filters | 6 -- src/core/db/CachedStatement.cpp | 74 -------------- src/core/db/CachedStatement.h | 71 ------------- src/core/db/Connection.cpp | 129 +++++------------------- src/core/db/Connection.h | 12 +-- src/core/db/Statement.h | 6 -- src/core/library/Indexer.cpp | 110 ++++++++++---------- src/core/library/Indexer.h | 7 +- src/core/library/track/IndexerTrack.cpp | 31 +++--- src/core/pch.hpp | 1 - 12 files changed, 106 insertions(+), 344 deletions(-) delete mode 100644 src/core/db/CachedStatement.cpp delete mode 100644 src/core/db/CachedStatement.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 034954c99..0fcd48f9f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,7 +4,6 @@ set(CORE_SOURCES ./audio/Player.cpp ./audio/Stream.cpp ./audio/GaplessTransport.cpp - ./db/CachedStatement.cpp ./db/Connection.cpp ./db/ScopedTransaction.cpp ./db/Statement.cpp diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index bdb57d67c..04a3e483d 100755 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -100,7 +100,6 @@ Create Create - @@ -148,7 +147,6 @@ - diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index be31c45c5..774e00be9 100755 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -40,9 +40,6 @@ src - - src\db - src\db @@ -120,9 +117,6 @@ src - - src\db - src\db diff --git a/src/core/db/CachedStatement.cpp b/src/core/db/CachedStatement.cpp deleted file mode 100644 index 3984746ea..000000000 --- a/src/core/db/CachedStatement.cpp +++ /dev/null @@ -1,74 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2007-2016 musikcube team -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the author nor the names of other contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -////////////////////////////////////////////////////////////////////////////// - -#include "pch.hpp" - -#include -#include -#include -#include - -using namespace musik::core::db; - -////////////////////////////////////////// -///\brief -///Constructor -/// -///\param sql -///SQL -/// -///\param connection -///Connection to execute the statement on -////////////////////////////////////////// -CachedStatement::CachedStatement(const char* sql,Connection &connection) : Statement(connection){ - this->sqlStatement.assign(sql); - this->stmt = this->connection->GetCachedStatement(sql); -} - -////////////////////////////////////////// -///\brief -///Destructor -/// -///Will return the cached statement to the Connection -/// -///\see -///musik::core::db::Connection::ReturnCachedStatement -////////////////////////////////////////// -CachedStatement::~CachedStatement(){ - sqlite3_reset(this->stmt); - sqlite3_clear_bindings(this->stmt); - this->connection->ReturnCachedStatement(this->sqlStatement.c_str(),this->stmt); - this->stmt=NULL; -} - diff --git a/src/core/db/CachedStatement.h b/src/core/db/CachedStatement.h deleted file mode 100644 index 84d94b9d2..000000000 --- a/src/core/db/CachedStatement.h +++ /dev/null @@ -1,71 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2007-2016 musikcube team -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the author nor the names of other contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include - -namespace musik{ namespace core{ namespace db{ - - // Forward declare - class Statement; - - ////////////////////////////////////////// - ///\brief - ///Same as Statement, but keeps the statement in a cache when destructed - /// - ///Be careful using this class only on "static" SQL statement - ///This means that you should not use this on statements looking like - ///this: CacheStatement("SELECT * FROM mytable WHERE id="+id,db); - ///since the cache will grow for each id in this example - /// - ///Instead, do like this: - ///CacheStatement("SELECT * FROM mytable WHERE id=?",db); - ///And use the BindInt to set the id - /// - ///\see - ///musik::core::db::Statement - ////////////////////////////////////////// - class CachedStatement : public Statement{ - public: - CachedStatement(const char* sql,Connection &connection); - ~CachedStatement(); - private: - std::string sqlStatement; - }; - - -} } } - diff --git a/src/core/db/Connection.cpp b/src/core/db/Connection.cpp index e43dd594f..38e79b963 100644 --- a/src/core/db/Connection.cpp +++ b/src/core/db/Connection.cpp @@ -43,27 +43,17 @@ using namespace musik::core::db; boost::mutex Connection::globalMutex; -////////////////////////////////////////// -///\brief -///Constructor -////////////////////////////////////////// -Connection::Connection() : connection(NULL),transactionCounter(0) { +Connection::Connection() +: connection(nullptr) +, transactionCounter(0) { this->Maintenance(true); } - -////////////////////////////////////////// -///\brief -///Destructor -/// -///Will automatically close the connection if it's not closed before -////////////////////////////////////////// Connection::~Connection(){ this->Close(); this->Maintenance(false); } - ////////////////////////////////////////// ///\brief ///Open a connection to the database @@ -80,19 +70,19 @@ Connection::~Connection(){ ///\returns ///Error code returned by SQLite ////////////////////////////////////////// -int Connection::Open(const char *database,unsigned int options,unsigned int cache){ -// sqlite3_enable_shared_cache(1); - +int Connection::Open(const char *database, unsigned int options, unsigned int cache) { int error; - #ifdef UTF_WIDECHAR - error = sqlite3_open16(database,&this->connection); - #else - error = sqlite3_open(database,&this->connection); - #endif - if(error==SQLITE_OK){ +#ifdef UTF_WIDECHAR + error = sqlite3_open16(database,&this->connection); +#else + error = sqlite3_open(database,&this->connection); +#endif + + if (error==SQLITE_OK) { this->Initialize(cache); } + return error; } @@ -116,19 +106,18 @@ int Connection::Open(const std::string &database,unsigned int options,unsigned i int error; #ifdef WIN32 std::wstring wdatabase = u8to16(database); - error = sqlite3_open16(wdatabase.c_str(),&this->connection); + error = sqlite3_open16(wdatabase.c_str(),&this->connection); #else - error = sqlite3_open(database.c_str(),&this->connection); + error = sqlite3_open(database.c_str(),&this->connection); #endif - if(error==SQLITE_OK){ + if (error==SQLITE_OK) { this->Initialize(cache); } + return error; } - - ////////////////////////////////////////// ///\brief ///Close connection to the database @@ -136,23 +125,15 @@ int Connection::Open(const std::string &database,unsigned int options,unsigned i ///\returns ///Errorcode ( musik::core::db::ReturnCode ) ////////////////////////////////////////// -int Connection::Close(){ - - // Clear the cache - for(StatementCache::iterator stmt=this->cachedStatements.begin();stmt!=this->cachedStatements.end();++stmt){ - sqlite3_finalize(stmt->second); - } - this->cachedStatements.clear(); - - - if(sqlite3_close(this->connection)==SQLITE_OK){ - this->connection = 0; +int Connection::Close() { + if (sqlite3_close(this->connection) == SQLITE_OK) { + this->connection = 0; return musik::core::db::Okay; } + return musik::core::db::Error; } - ////////////////////////////////////////// ///\brief ///Execute a SQL string @@ -166,7 +147,7 @@ int Connection::Close(){ ///\see ///musik::core::db::ReturnCode ////////////////////////////////////////// -int Connection::Execute(const char* sql){ +int Connection::Execute(const char* sql) { sqlite3_stmt *stmt = NULL; // Prepaire seems to give errors when interrupted @@ -187,6 +168,7 @@ int Connection::Execute(const char* sql){ sqlite3_reset(stmt); sqlite3_finalize(stmt); + return musik::core::db::Okay; } @@ -204,7 +186,7 @@ int Connection::Execute(const char* sql){ ///\see ///musik::core::db::ReturnCode ////////////////////////////////////////// -int Connection::Execute(const wchar_t* sql){ +int Connection::Execute(const wchar_t* sql) { sqlite3_stmt *stmt = NULL; { boost::mutex::scoped_lock lock(this->mutex); @@ -216,7 +198,7 @@ int Connection::Execute(const wchar_t* sql){ } // Execute the statement - int error = this->StepStatement(stmt); + int error = this->StepStatement(stmt); if(error!=SQLITE_OK && error!=SQLITE_DONE){ sqlite3_finalize(stmt); return db::Error; @@ -232,7 +214,6 @@ void Connection::Analyze(){ // this->Execute("ANALYZE"); } - ////////////////////////////////////////// ///\brief ///Get the last inserted row ID @@ -244,10 +225,9 @@ void Connection::Analyze(){ ///http://www.sqlite.org/c3ref/last_insert_rowid.html ////////////////////////////////////////// int Connection::LastInsertedId(){ - return (int)sqlite3_last_insert_rowid(this->connection); + return (int) sqlite3_last_insert_rowid(this->connection); } - ////////////////////////////////////////// ///\brief ///Initializes the database. @@ -282,65 +262,6 @@ void Connection::Initialize(unsigned int cache){ } - -////////////////////////////////////////// -///\brief -///Internal method used by the CachedStatement to locate if a statement already exists -/// -///\param sql -///SQL to check for -/// -///\returns -///The cached or newly created statement -/// -///\see -///musik::core::db::CachedStatment -////////////////////////////////////////// -sqlite3_stmt *Connection::GetCachedStatement(const char* sql){ - sqlite3_stmt *newStmt(NULL); - - StatementCache::iterator stmt = this->cachedStatements.find(sql); - if(stmt==this->cachedStatements.end()){ - - boost::mutex::scoped_lock lock(this->mutex); - - int err = sqlite3_prepare_v2(this->connection,sql,-1,&newStmt,NULL); - if(err!=SQLITE_OK){ - return NULL; - } - return newStmt; - } - - newStmt = stmt->second; - this->cachedStatements.erase(stmt); - return newStmt; -} - - -////////////////////////////////////////// -///\brief -///Used by CachedStatement when destructed to return it's statement. -/// -///\param sql -///SQL string -/// -///\param stmt -///Statement to return -/// -///\see -///musik::core::db::CachedStatment -////////////////////////////////////////// -void Connection::ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt){ - StatementCache::iterator cacheStmt = this->cachedStatements.find(sql); - if(cacheStmt==this->cachedStatements.end()){ - // Insert the stmt in cache - this->cachedStatements[sql] = stmt; - }else{ - // Stmt already exists. Finalize it - DB_ASSERT(sqlite3_finalize(stmt)); - } -} - ////////////////////////////////////////// ///\brief ///Interrupts the current running statement(s) diff --git a/src/core/db/Connection.h b/src/core/db/Connection.h index 885151284..1c66bc048 100644 --- a/src/core/db/Connection.h +++ b/src/core/db/Connection.h @@ -76,20 +76,12 @@ namespace musik{ namespace core{ namespace db{ void Initialize(unsigned int cache); - typedef std::map StatementCache; - StatementCache cachedStatements; - friend class Statement; - friend class CachedStatement; friend class ScopedTransaction; - - sqlite3_stmt *GetCachedStatement(const char* sql); - void ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt); - + int StepStatement(sqlite3_stmt *stmt); - int transactionCounter; - + int transactionCounter; sqlite3 *connection; boost::mutex mutex; diff --git a/src/core/db/Statement.h b/src/core/db/Statement.h index 7168124c2..1dd3185a9 100644 --- a/src/core/db/Statement.h +++ b/src/core/db/Statement.h @@ -48,8 +48,6 @@ struct sqlite3_stmt; namespace musik{ namespace core{ namespace db{ class Connection; - class CachedStatement; - ////////////////////////////////////////// ///\brief @@ -87,9 +85,5 @@ namespace musik{ namespace core{ namespace db{ Statement(Connection &connection); }; - - } } } -#include - diff --git a/src/core/library/Indexer.cpp b/src/core/library/Indexer.cpp index 2691eb07c..74293543f 100644 --- a/src/core/library/Indexer.cpp +++ b/src/core/library/Indexer.cpp @@ -52,11 +52,14 @@ #include static const std::string TAG = "Indexer"; +static const int MAX_THREADS = 8; using namespace musik::core; using namespace musik::core::metadata; using namespace musik::core::audio; +using Thread = std::unique_ptr; + static std::string normalizeDir(std::string path) { path = boost::filesystem::path(path).make_preferred().string(); @@ -84,12 +87,6 @@ Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename) this->thread = new boost::thread(boost::bind(&Indexer::ThreadLoop, this)); } -////////////////////////////////////////// -///\brief -///Destructor -/// -///Exits and joins threads -////////////////////////////////////////// Indexer::~Indexer() { if (this->thread) { this->Exit(); @@ -99,38 +96,17 @@ Indexer::~Indexer() { } } -////////////////////////////////////////// -///\brief -///Restart the sync -/// -///\param bNewRestart -///Should if be restarted or not -////////////////////////////////////////// void Indexer::Synchronize(bool restart) { boost::mutex::scoped_lock lock(this->exitMutex); this->restart = restart; this->Notify(); } -////////////////////////////////////////// -///\brief -///Should the sync be restarted? -////////////////////////////////////////// bool Indexer::Restarted() { boost::mutex::scoped_lock lock(this->exitMutex); return this->restart; } -////////////////////////////////////////// -///\brief -///Add a new path to the Indexer. -/// -///\param sPath -///Path to add -/// -///\remarks -///If the path already exists it will not be added. -////////////////////////////////////////// void Indexer::AddPath(const std::string& path) { Indexer::AddRemoveContext context; context.add = true; @@ -144,13 +120,6 @@ void Indexer::AddPath(const std::string& path) { this->Synchronize(true); } -////////////////////////////////////////// -///\brief -///Remove a path from the Indexer -/// -///\param sPath -///Path to remove -////////////////////////////////////////// void Indexer::RemovePath(const std::string& path) { Indexer::AddRemoveContext context; context.add = false; @@ -164,10 +133,6 @@ void Indexer::RemovePath(const std::string& path) { this->Synchronize(true); } -////////////////////////////////////////// -///\brief -///Main method for doing the synchronization. -////////////////////////////////////////// void Indexer::SynchronizeInternal() { /* load all of the metadata (tag) reader plugins */ typedef PluginFactory::DestroyDeleter MetadataDeleter; @@ -252,7 +217,7 @@ void Indexer::SynchronizeInternal() { } - if(!this->Restarted() && !this->Exited()){ + if (!this->Restarted() && !this->Exited()){ this->SyncCleanup(); } @@ -265,7 +230,7 @@ void Indexer::SynchronizeInternal() { this->status = 5; } - if(!this->Restarted() && !this->Exited()){ + if (!this->Restarted() && !this->Exited()){ this->SyncOptimize(); } @@ -282,13 +247,13 @@ void Indexer::SynchronizeInternal() { } void Indexer::ReadMetadataFromFile( - const boost::filesystem::directory_iterator file, + const boost::filesystem::path& file, const std::string& pathId) { musik::core::IndexerTrack track(0); /* get cached filesize, parts, size, etc */ - if (!track.NeedsToBeIndexed(file->path(), this->dbConnection)) { + if (!track.NeedsToBeIndexed(file, this->dbConnection)) { return; } @@ -299,7 +264,7 @@ void Indexer::ReadMetadataFromFile( Iterator it = this->metadataReaders.begin(); while (it != this->metadataReaders.end()) { if ((*it)->CanRead(track.GetValue("extension").c_str())) { - if ((*it)->Read(file->path().string().c_str(), &track)) { + if ((*it)->Read(file.string().c_str(), &track)) { saveToDb = true; break; } @@ -310,12 +275,12 @@ void Indexer::ReadMetadataFromFile( /* no tag? well... if a decoder can play it, add it to the database with the file as the name. */ if (!saveToDb) { - std::string fullPath = file->path().string(); + std::string fullPath = file.string(); auto it = this->audioDecoders.begin(); while (it != this->audioDecoders.end()) { if ((*it)->CanHandle(fullPath.c_str())) { saveToDb = true; - track.SetValue("title", file->path().leaf().string().c_str()); + track.SetValue("title", file.leaf().string().c_str()); break; } ++it; @@ -326,15 +291,30 @@ void Indexer::ReadMetadataFromFile( if (saveToDb) { track.SetValue("path_id", pathId.c_str()); track.Save(this->dbConnection, this->libraryPath); + } +} - this->filesSaved++; - if (this->filesSaved % 200 == 0) { - if (this->trackTransaction) { - this->trackTransaction->CommitAndRestart(); - } +static inline void joinAndNotify( + std::vector& threads, + std::shared_ptr transaction, + sigslot::signal0<>& event, + size_t& total) +{ + total += threads.size(); - this->TrackRefreshed(); + for (size_t i = 0; i < threads.size(); i++) { + threads.at(i)->join(); + } + + threads.clear(); + + if (total > 200) { + if (transaction) { + transaction->CommitAndRestart(); } + + event(); + total = 0; } } @@ -362,21 +342,47 @@ void Indexer::SyncDirectory( boost::filesystem::directory_iterator file(path); std::string pathIdStr = boost::lexical_cast(pathId); + std::vector threads; + + #define WAIT_FOR_ACTIVE() \ + joinAndNotify( \ + threads, \ + this->trackTransaction, \ + this->TrackRefreshed, \ + this->filesSaved); for( ; file != end && !this->Exited() && !this->Restarted(); file++) { + /* we do things in batches of 5. wait for this batch to + finish, then we'll spin up some more... */ + if (threads.size() >= MAX_THREADS) { + WAIT_FOR_ACTIVE(); + } + if (is_directory(file->status())) { /* recursion here */ musik::debug::info(TAG, "scanning " + file->path().string()); + WAIT_FOR_ACTIVE(); this->SyncDirectory(syncRoot, file->path().string(), pathId); } else { ++this->filesIndexed; - this->ReadMetadataFromFile(file, pathIdStr); + + threads.push_back(Thread(new boost::thread( + boost::bind( + &Indexer::ReadMetadataFromFile, + this, + file->path(), + pathIdStr)))); } } + + /* there may be a few left... */ + WAIT_FOR_ACTIVE(); } catch(...) { } + + #undef WAIT_FOR_ACTIVE() } void Indexer::ThreadLoop() { diff --git a/src/core/library/Indexer.h b/src/core/library/Indexer.h index 71c2a0521..9e9163fce 100644 --- a/src/core/library/Indexer.h +++ b/src/core/library/Indexer.h @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -88,7 +89,7 @@ namespace musik { namespace core { DBID pathId); void ReadMetadataFromFile( - const boost::filesystem::directory_iterator path, + const boost::filesystem::path& path, const std::string& pathId); db::Connection dbConnection; @@ -102,8 +103,8 @@ namespace musik { namespace core { boost::thread *thread; boost::mutex progressMutex; - int filesIndexed; - int filesSaved; + size_t filesIndexed; + size_t filesSaved; struct AddRemoveContext { bool add; diff --git a/src/core/library/track/IndexerTrack.cpp b/src/core/library/track/IndexerTrack.cpp index c7bea03d0..bdd43f8fd 100644 --- a/src/core/library/track/IndexerTrack.cpp +++ b/src/core/library/track/IndexerTrack.cpp @@ -43,6 +43,7 @@ #include #include +#include using namespace musik::core; @@ -56,6 +57,8 @@ using namespace musik::core; #define ARTIST_TRACK_JUNCTION_TABLE_NAME "track_artists" #define ARTIST_TRACK_FOREIGN_KEY "artist_id" +static boost::mutex trackWriteLock; + IndexerTrack::IndexerTrack(DBID id) : internalMetadata(new IndexerTrack::MetadataWithThumbnail()) , id(id) @@ -147,7 +150,7 @@ bool IndexerTrack::NeedsToBeIndexed( this->SetValue("filesize", boost::lexical_cast(fileSize).c_str()); this->SetValue("filetime", boost::lexical_cast(fileTime).c_str()); - db::CachedStatement stmt( + db::Statement stmt( "SELECT id, filename, filesize, filetime " \ "FROM tracks t " \ "WHERE filename=?", dbConnection); @@ -176,7 +179,7 @@ static DBID writeToTracksTable( db::Connection &dbConnection, IndexerTrack& track) { - db::CachedStatement stmt("INSERT OR REPLACE INTO tracks " \ + db::Statement stmt("INSERT OR REPLACE INTO tracks " \ "(id, track, disc, bpm, duration, filesize, year, title, filename, filetime, path_id) " \ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", dbConnection); @@ -210,7 +213,7 @@ static void removeRelation( DBID trackId) { std::string query = boost::str(boost::format("DELETE FROM %1% WHERE track_id=?") % field); - db::CachedStatement stmt(query.c_str(), connection); + db::Statement stmt(query.c_str(), connection); stmt.BindInt(0, trackId); stmt.Step(); } @@ -239,7 +242,7 @@ DBID IndexerTrack::SaveThumbnail(db::Connection& connection, const std::string& if (this->internalMetadata->thumbnailData) { uint64 sum = Checksum(this->internalMetadata->thumbnailData, this->internalMetadata->thumbnailSize); - db::CachedStatement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?", connection); + db::Statement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?", connection); thumbs.BindInt(0, this->internalMetadata->thumbnailSize); thumbs.BindInt(1, sum); @@ -280,11 +283,11 @@ void IndexerTrack::ProcessNonStandardMetadata(db::Connection& connection) { MetadataMap unknownFields(this->internalMetadata->metadata); removeKnownFields(unknownFields); - db::CachedStatement selectMetaKey("SELECT id FROM meta_keys WHERE name=?", connection); - db::CachedStatement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?", connection); - db::CachedStatement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)", connection); - db::CachedStatement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)", connection); - db::CachedStatement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)", connection); + db::Statement selectMetaKey("SELECT id FROM meta_keys WHERE name=?", connection); + db::Statement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?", connection); + db::Statement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)", connection); + db::Statement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)", connection); + db::Statement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)", connection); MetadataMap::const_iterator it = unknownFields.begin(); for ( ; it != unknownFields.end(); ++it){ @@ -357,7 +360,7 @@ DBID IndexerTrack::SaveSingleValueField( std::string selectQuery = boost::str(boost::format( "SELECT id FROM %1% WHERE name=?") % fieldTableName); - db::CachedStatement stmt(selectQuery.c_str(), dbConnection); + db::Statement stmt(selectQuery.c_str(), dbConnection); std::string value = this->GetValue(trackMetadataKeyName.c_str()); stmt.BindText(0, value); @@ -450,7 +453,7 @@ DBID IndexerTrack::SaveArtist(db::Connection& dbConnection) { } bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirectory) { - db::ScopedTransaction transaction(dbConnection); + boost::mutex::scoped_lock lock(trackWriteLock); if (this->GetValue("album_artist") == "") { this->SetValue("album_artist", this->GetValue("artist").c_str()); @@ -477,7 +480,7 @@ bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirecto /* update all of the track foreign keys */ { - db::CachedStatement stmt( + db::Statement stmt( "UPDATE tracks " \ "SET album_id=?, visual_genre_id=?, visual_artist_id=?, album_artist_id=?, thumbnail_id=? " \ "WHERE id=?", dbConnection); @@ -510,7 +513,7 @@ DBID IndexerTrack::SaveNormalizedFieldValue( { std::string query = boost::str(boost::format("SELECT id FROM %1% WHERE name=?") % tableName); - db::CachedStatement stmt(query.c_str(), dbConnection); + db::Statement stmt(query.c_str(), dbConnection); stmt.BindText(0, fieldValue); if (stmt.Step() == db::Row) { @@ -524,7 +527,7 @@ DBID IndexerTrack::SaveNormalizedFieldValue( std::string query = boost::str(boost::format( "INSERT INTO %1% (name, aggregated) VALUES (?, ?)") % tableName); - db::CachedStatement stmt(query.c_str(), dbConnection); + db::Statement stmt(query.c_str(), dbConnection); stmt.BindText(0, fieldValue); stmt.BindInt(1, isAggregatedValue ? 1 : 0); diff --git a/src/core/pch.hpp b/src/core/pch.hpp index 2293cbd25..a15dcb151 100644 --- a/src/core/pch.hpp +++ b/src/core/pch.hpp @@ -41,5 +41,4 @@ #include #include - #include #endif \ No newline at end of file