/*************************************************************************
* 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