/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011 Martin Raiber * * This program 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. * * This program 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 this program. If not, see . **************************************************************************/ #include "vld.h" #include "Server.h" #include "Query.h" #include "sqlite/sqlite3.h" IMutex * CDatabase::lock_mutex=NULL; int CDatabase::lock_count=0; ICondition *CDatabase::unlock_cond=NULL; /* #ifdef _WIN32 #ifdef _DEBUG #pragma comment ( lib , "sqlite/sqlite3_dll_debug.lib" ) #else if _RELEASE #pragma comment ( lib , "sqlite/sqlite3_dll_release.lib" ) #endif #endif*/ struct UnlockNotification { bool fired; ICondition* cond; IMutex *mutex; }; static int callback(void *CPtr, int argc, char **argv, char **azColName) { CDatabase* db=(CDatabase*)CPtr; db_nsingle_result result; for(int i=0; i(azColName[i], argv[i]) ); } db->InsertResults(result); return 0; } static void unlock_notify_cb(void **apArg, int nArg) { for(int i=0; imutex); p->fired = true; p->cond->notify_all(); } } CDatabase::~CDatabase() { destroyAllQueries(); for(std::map::iterator iter=prepared_queries.begin();iter!=prepared_queries.end();++iter) { CQuery *q=(CQuery*)iter->second; delete q; } prepared_queries.clear(); sqlite3_close(db); } bool CDatabase::Open(std::string pFile) { in_transaction=false; if( sqlite3_open(pFile.c_str(), &db) ) { Server->Log("Could not open db ["+pFile+"]"); sqlite3_close(db); return false; } else { sqlite3_busy_timeout(db, 50); Write("PRAGMA foreign_keys = ON"); Write("PRAGMA synchronous=NORMAL"); return true; } } void CDatabase::initMutex(void) { lock_mutex=Server->createMutex(); unlock_cond=Server->createCondition(); } void CDatabase::destroyMutex(void) { Server->destroy(lock_mutex); Server->destroy(unlock_cond); } db_nresults CDatabase::ReadN(std::string pQuery) { //Server->Log("SQL Query(Read): "+pQuery); results.clear(); char *zErrMsg = 0; int rc=sqlite3_exec(db, pQuery.c_str(), callback, this, &zErrMsg); if( rc!=SQLITE_OK ) { Server->Log("SQL ERROR: "+(std::string)zErrMsg); } if( zErrMsg!=NULL ) sqlite3_free(zErrMsg); return results; } db_results CDatabase::Read(std::string pQuery) { //Server->Log("SQL Query(Read): "+pQuery, LL_DEBUG); IQuery *q=Prepare(pQuery, false); db_results ret=q->Read(); delete ((CQuery*)q); return ret; } bool CDatabase::Write(std::string pQuery) { //Server->Log("SQL Query(Write): "+pQuery, LL_DEBUG); IQuery *q=Prepare(pQuery, false); if(q!=NULL) { bool b=q->Write(); delete ((CQuery*)q); return b; } else { return false; } } void CDatabase::InsertResults(const db_nsingle_result &pResult) { results.push_back(pResult); } //ToDo: Cache Writings void CDatabase::BeginTransaction(void) { Write("BEGIN IMMEDIATE;"); in_transaction=true; } bool CDatabase::EndTransaction(void) { Write("END;"); in_transaction=false; IScopedLock lock(lock_mutex); bool waited=false; while(lock_count>0) { unlock_cond->wait(&lock); waited=true; } if(waited) { Server->wait(1000); } return true; } IQuery* CDatabase::Prepare(std::string pQuery, bool autodestroy) { sqlite3_stmt *prepared_statement; const char* tail; int err; bool transaction_lock=false; while((err=sqlite3_prepare_v2(db, pQuery.c_str(), (int)pQuery.size(), &prepared_statement, &tail) )==SQLITE_LOCKED || err==SQLITE_BUSY) { if(err==SQLITE_LOCKED) { if(LockForTransaction()) { transaction_lock=true; if(!WaitForUnlock()) Server->Log("DATABASE DEADLOCKED in CDatabase::Prepare", LL_ERROR); } } else { if(transaction_lock==false) { if(!isInTransaction() && LockForTransaction()) { transaction_lock=true; } sqlite3_busy_timeout(db, 10000); } else { Server->Log("DATABASE BUSY in CDatabase::Prepare", LL_ERROR); } } } if(transaction_lock) { UnlockForTransaction(); sqlite3_busy_timeout(db, 50); } if( err!=SQLITE_OK ) { Server->Log("Error preparing Query ["+pQuery+"]: "+sqlite3_errmsg(db),LL_ERROR); return NULL; } CQuery *q=new CQuery(pQuery, prepared_statement, this); if( autodestroy ) queries.push_back(q); return q; } IQuery* CDatabase::Prepare(int id, std::string pQuery) { std::map::iterator iter=prepared_queries.find(id); if( iter!=prepared_queries.end() ) { iter->second->Reset(); return iter->second; } else { IQuery *q=Prepare(pQuery, false); prepared_queries.insert(std::pair(id, q) ); return q; } } void CDatabase::destroyQuery(IQuery *q) { for(size_t i=0;icreateMutex(); un.cond=Server->createCondition(); rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un); if( rc==SQLITE_OK ) { IScopedLock lock(un.mutex); if( !un.fired ) { un.cond->wait(&lock); } } Server->destroy(un.mutex); Server->destroy(un.cond); return rc==SQLITE_OK; } sqlite3 *CDatabase::getDatabase(void) { return db; } bool CDatabase::LockForTransaction(void) { lock_mutex->Lock(); ++lock_count; return true; } void CDatabase::UnlockForTransaction(void) { --lock_count; lock_mutex->Unlock(); unlock_cond->notify_all(); } bool CDatabase::isInTransaction(void) { return in_transaction; }