/************************************************************************* * 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 . **************************************************************************/ #ifndef NO_SQLITE #if defined(_WIN32) || defined(WIN32) #define _CRT_SECURE_NO_WARNINGS #endif #include "vld.h" #include "Interface/File.h" #ifndef BDBPLUGIN #include "Server.h" #else #ifdef LINUX #include "bdbplugin/config.h" #include DB_HEADER #else #include #endif #include "Interface/Server.h" #endif #include "Query.h" #include "sqlite/sqlite3.h" extern "C" { #include "sqlite/shell.h" } #include "Database.h" #include "stringtools.h" IMutex * CDatabase::lock_mutex=NULL; int CDatabase::lock_count=0; ICondition *CDatabase::unlock_cond=NULL; 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, const std::vector > &attach) { attached_dbs=attach; in_transaction=false; if( sqlite3_open(pFile.c_str(), &db) ) { Server->Log("Could not open db ["+pFile+"]"); sqlite3_close(db); return false; } else { #ifdef BDBPLUGIN /*db_results res=Read("PRAGMA multiversion"); if(!res.empty() && res[0][L"multiversion"]!=L"1") { Write("PRAGMA multiversion=ON"); }*/ Write("PRAGMA synchronous=ON"); //Write("PRAGMA snapshot_isolation=ON"); //Write("PRAGMA bdbsql_error_file='urbackup/bdb_errors.log'"); #else Write("PRAGMA synchronous=NORMAL"); #endif Write("PRAGMA foreign_keys = ON"); sqlite3_busy_timeout(db, 50); AttachDBs(); 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); if(q!=NULL) { db_results ret=q->Read(); delete ((CQuery*)q); return ret; } return db_results(); } 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(50); } 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); if(err==SQLITE_IOERR) { Server->setFailBit(IServer::FAIL_DATABASE_IOERR); } if(err==SQLITE_CORRUPT) { Server->setFailBit(IServer::FAIL_DATABASE_CORRUPTED); } 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; #else return false; #endif } sqlite3 *CDatabase::getDatabase(void) { return db; } bool CDatabase::LockForTransaction(void) { lock_mutex->Lock(); ++lock_count; return true; } void CDatabase::UnlockForTransaction(void) { --lock_count; unlock_cond->notify_all(); lock_mutex->Unlock(); } bool CDatabase::isInTransaction(void) { return in_transaction; } bool CDatabase::Import(const std::string &pFile) { IFile *file=Server->openFile(pFile, MODE_READ); if(file==NULL) return false; unsigned int r; char buf[4096]; std::string query; int state=0; do { r=file->Read(buf, 4096); for(unsigned int i=0;i0); Server->destroy(file); return true; } bool CDatabase::Dump(const std::string &pFile) { callback_data cd; cd.db=db; cd.out=fopen(pFile.c_str(), "wb"); if(cd.out==0) { return false; } char *cmd=new char[6]; cmd[0]='.'; cmd[1]='d'; cmd[2]='u'; cmd[3]='m'; cmd[4]='p'; cmd[5]=0; do_meta_command_r(cmd, &cd); delete []cmd; fclose(cd.out); return true; } std::string CDatabase::getEngineName(void) { #ifndef BDBPLUGIN return "sqlite"; #else return "bdb"; #endif } void CDatabase::AttachDBs(void) { for(size_t i=0;iwait(250); } while( rc==SQLITE_OK || rc==SQLITE_BUSY || rc==SQLITE_LOCKED ); /* Release resources allocated by backup_init(). */ (void)sqlite3_backup_finish(pBackup); } else { Server->Log("Opening backup connection failed", LL_INFO); } rc = sqlite3_errcode(pBackupDB); if(rc!=0) { Server->Log("Database backup failed with error code: "+nconvert(rc)+" err: "+sqlite3_errmsg(pBackupDB), LL_INFO); } } /* Close the database connection opened on database file zFilename ** and return the result of this function. */ (void)sqlite3_close(pBackupDB); return rc==0; } bool CDatabase::Backup(const std::string &pFile) { std::string path=ExtractFilePath(pFile); bool b=backup_db(pFile, "main"); if(!b) return false; for(size_t i=0;i