00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #ifdef HAVE_SQLITE3
00037
00038 #include "sqlite3_storage.h"
00039
00040 #include "common.h"
00041 #include "config_manager.h"
00042
00043 #ifdef AUTO_CREATE_DATABASE
00044 #include "sqlite3_create_sql.h"
00045 #include <zlib.h>
00046 #endif
00047
00048
00049 #define SQLITE3_UPDATE_1_2_1 "DROP INDEX mt_autoscan_obj_id"
00050 #define SQLITE3_UPDATE_1_2_2 "CREATE UNIQUE INDEX mt_autoscan_obj_id ON mt_autoscan(obj_id)"
00051 #define SQLITE3_UPDATE_1_2_3 "ALTER TABLE \"mt_autoscan\" ADD \"path_ids\" text"
00052 #define SQLITE3_UPDATE_1_2_4 "UPDATE \"mt_internal_setting\" SET \"value\"='2' WHERE \"key\"='db_version' AND \"value\"='1'"
00053
00054
00055 #define SQLITE3_UPDATE_2_3_1 "ALTER TABLE \"mt_cds_object\" ADD \"service_id\" varchar(255) default NULL"
00056 #define SQLITE3_UPDATE_2_3_2 "CREATE INDEX mt_cds_object_service_id ON mt_cds_object(service_id)"
00057 #define SQLITE3_UPDATE_2_3_3 "UPDATE \"mt_internal_setting\" SET \"value\"='3' WHERE \"key\"='db_version' AND \"value\"='2'"
00058
00059 #define SL3_INITITAL_QUEUE_SIZE 20
00060
00061 using namespace zmm;
00062 using namespace mxml;
00063
00064 Sqlite3Storage::Sqlite3Storage() : SQLStorage()
00065 {
00066 shutdownFlag = false;
00067 table_quote_begin = '"';
00068 table_quote_end = '"';
00069 startupError = nil;
00070 sqliteMutex = Ref<Mutex>(new Mutex());
00071 cond = Ref<Cond>(new Cond(sqliteMutex));
00072 insertBuffer = nil;
00073 dirty = false;
00074 }
00075
00076 void Sqlite3Storage::init()
00077 {
00078 SQLStorage::init();
00079
00080 int ret;
00081
00082 AUTOLOCK(sqliteMutex);
00083
00084
00085
00086
00087
00088
00089 String dbFilePath = ConfigManager::getInstance()->getOption(CFG_SERVER_STORAGE_SQLITE_DATABASE_FILE);
00090
00091
00092 if (access(dbFilePath.c_str(), R_OK | W_OK) != 0 && errno != ENOENT)
00093 throw _StorageException(nil, _("Error while accessing sqlite database file (") + dbFilePath +"): " + mt_strerror(errno));
00094
00095
00096
00097 taskQueue = Ref<ObjectQueue<SLTask> >(new ObjectQueue<SLTask>(SL3_INITITAL_QUEUE_SIZE));
00098 taskQueueOpen = true;
00099
00100 ret = pthread_create(
00101 &sqliteThread,
00102 NULL,
00103 Sqlite3Storage::staticThreadProc,
00104 this
00105 );
00106
00107
00108 cond->wait();
00109 AUTOUNLOCK();
00110 if (startupError != nil)
00111 throw _StorageException(nil, startupError);
00112
00113
00114 String dbVersion = nil;
00115 try
00116 {
00117 dbVersion = getInternalSetting(_("db_version"));
00118 }
00119 catch (Exception)
00120 {
00121 log_warning("Sqlite3 database seems to be corrupt or doesn't exist yet.\n");
00122
00123 if (ConfigManager::getInstance()->getBoolOption(CFG_SERVER_STORAGE_SQLITE_RESTORE))
00124 {
00125
00126
00127
00128 String dbFilePathbackup = dbFilePath + ".backup";
00129 if (access(dbFilePathbackup.c_str(), R_OK) == 0)
00130 {
00131 try
00132 {
00133
00134 Ref<SLBackupTask> btask (new SLBackupTask(true));
00135 this->addTask(RefCast(btask, SLTask));
00136 btask->waitForTask();
00137 dbVersion = getInternalSetting(_("db_version"));
00138 }
00139 catch (Exception)
00140 {
00141 }
00142 }
00143
00144 if (dbVersion == nil)
00145 {
00146 #ifdef AUTO_CREATE_DATABASE
00147 log_info("no sqlite3 backup is available or backup is corrupt. automatically creating database...\n");
00148 Ref<SLInitTask> ptask (new SLInitTask());
00149 addTask(RefCast(ptask, SLTask));
00150 try
00151 {
00152 ptask->waitForTask();
00153 dbVersion = getInternalSetting(_("db_version"));
00154 }
00155 catch (Exception e)
00156 {
00157 shutdown();
00158 throw _Exception(_("error while creating database: ") + e.getMessage());
00159 }
00160 log_info("database created successfully.\n");
00161 #else
00162 shutdown();
00163 throw _Exception(_("database doesn't seem to exist yet and autocreation wasn't compiled in"));
00164 #endif
00165 }
00166 }
00167 else
00168 {
00169
00170 shutdown();
00171 throw _Exception(_("sqlite3 database seems to be corrupt and the 'on-error' option is set to 'fail'"));
00172 }
00173 }
00174
00175 if (dbVersion == nil)
00176 {
00177 shutdown();
00178 throw _Exception(_("sqlite3 database seems to be corrupt and restoring from backup failed"));
00179 }
00180
00181
00182 _exec("PRAGMA locking_mode = EXCLUSIVE");
00183 int synchronousOption = ConfigManager::getInstance()->getIntOption(CFG_SERVER_STORAGE_SQLITE_SYNCHRONOUS);
00184 Ref<StringBuffer> buf(new StringBuffer());
00185 *buf << "PRAGMA synchronous = " << synchronousOption;
00186 SQLStorage::exec(buf);
00187
00188
00189
00190 log_debug("db_version: %s\n", dbVersion.c_str());
00191
00192
00193
00194 if (dbVersion == "1")
00195 {
00196 log_info("Doing an automatic database upgrade from database version 1 to version 2...\n");
00197 _exec(SQLITE3_UPDATE_1_2_1);
00198 _exec(SQLITE3_UPDATE_1_2_2);
00199 _exec(SQLITE3_UPDATE_1_2_3);
00200 _exec(SQLITE3_UPDATE_1_2_4);
00201 log_info("database upgrade successful.\n");
00202 dbVersion = _("2");
00203 }
00204
00205 if (dbVersion == "2")
00206 {
00207 log_info("Doing an automatic database upgrade from database version 2 to version 3...\n");
00208 _exec(SQLITE3_UPDATE_2_3_1);
00209 _exec(SQLITE3_UPDATE_2_3_2);
00210 _exec(SQLITE3_UPDATE_2_3_3);
00211 log_info("database upgrade successful.\n");
00212 dbVersion = _("3");
00213 }
00214
00215
00216
00217 if (! string_ok(dbVersion) || dbVersion != "3")
00218 throw _Exception(_("The database seems to be from a newer version!"));
00219
00220
00221
00222 if (ConfigManager::getInstance()->getBoolOption(CFG_SERVER_STORAGE_SQLITE_BACKUP_ENABLED))
00223 {
00224 int backupInterval = ConfigManager::getInstance()->getIntOption(CFG_SERVER_STORAGE_SQLITE_BACKUP_INTERVAL);
00225 Ref<TimerSubscriberObject> backupTimerSubscriber(new Sqlite3BackupTimerSubscriber());
00226 Timer::getInstance()->addTimerSubscriber(backupTimerSubscriber, backupInterval, Ref<Object>(this));
00227
00228
00229 Ref<SLBackupTask> btask (new SLBackupTask(false));
00230 this->addTask(RefCast(btask, SLTask));
00231 btask->waitForTask();
00232 }
00233
00234 dbReady();
00235 }
00236
00237 void Sqlite3Storage::_exec(const char *query)
00238 {
00239 exec(query, strlen(query), false);
00240 }
00241
00242 String Sqlite3Storage::quote(String value)
00243 {
00244 char *q = sqlite3_mprintf("'%q'",
00245 (value == nil ? "" : value.c_str()));
00246 String ret = q;
00247 sqlite3_free(q);
00248 return ret;
00249 }
00250
00251 String Sqlite3Storage::getError(String query, String error, sqlite3 *db)
00252 {
00253 return _("SQLITE3: (") + sqlite3_errcode(db) + ") "
00254 + sqlite3_errmsg(db) +"\nQuery:" + (query == nil ? _("unknown") : query) + "\nerror: " + (error == nil ? _("unknown") : error);
00255 }
00256
00257 Ref<SQLResult> Sqlite3Storage::select(const char *query, int length)
00258 {
00259
00260
00261 Ref<SLSelectTask> ptask (new SLSelectTask(query));
00262 addTask(RefCast(ptask, SLTask));
00263 ptask->waitForTask();
00264 return ptask->getResult();
00265 }
00266
00267 int Sqlite3Storage::exec(const char *query, int length, bool getLastInsertId)
00268 {
00269
00270
00271 Ref<SLExecTask> ptask (new SLExecTask(query, getLastInsertId));
00272 addTask(RefCast(ptask, SLTask));
00273 ptask->waitForTask();
00274 if (getLastInsertId) return ptask->getLastInsertId();
00275 else return -1;
00276 }
00277
00278 void *Sqlite3Storage::staticThreadProc(void *arg)
00279 {
00280 Sqlite3Storage *inst = (Sqlite3Storage *)arg;
00281 inst->threadProc();
00282 log_debug("Sqlite3Storage::staticThreadProc - exiting thread\n");
00283 pthread_exit(NULL);
00284 return NULL;
00285 }
00286
00287 void Sqlite3Storage::threadProc()
00288 {
00289 Ref<SLTask> task;
00290
00291 sqlite3 *db;
00292
00293 String dbFilePath = ConfigManager::getInstance()->getOption(CFG_SERVER_STORAGE_SQLITE_DATABASE_FILE);
00294
00295 int res = sqlite3_open(dbFilePath.c_str(), &db);
00296 if(res != SQLITE_OK)
00297 {
00298 startupError = _("Sqlite3Storage.init: could not open ") +
00299 dbFilePath;
00300 return;
00301 }
00302 AUTOLOCK(sqliteMutex);
00303
00304 cond->signal();
00305
00306 while(! shutdownFlag)
00307 {
00308 if((task = taskQueue->dequeue()) == nil)
00309 {
00310
00311 cond->wait();
00312 continue;
00313 }
00314 AUTOUNLOCK();
00315 try
00316 {
00317 task->run(&db, this);
00318 if (task->didContamination())
00319 dirty = true;
00320 else if (task->didDecontamination())
00321 dirty = false;
00322 task->sendSignal();
00323 }
00324 catch (Exception e)
00325 {
00326 task->sendSignal(e.getMessage());
00327 }
00328 AUTORELOCK();
00329 }
00330
00331 taskQueueOpen = false;
00332 while((task = taskQueue->dequeue()) != nil)
00333 {
00334 task->sendSignal(_("Sorry, sqlite3 thread is shutting down"));
00335 }
00336 if (db)
00337 sqlite3_close(db);
00338 }
00339
00340 void Sqlite3Storage::addTask(zmm::Ref<SLTask> task, bool onlyIfDirty)
00341 {
00342 if (! taskQueueOpen)
00343 throw _Exception(_("sqlite3 task queue is already closed"));
00344 AUTOLOCK(sqliteMutex);
00345 if (! taskQueueOpen)
00346 {
00347 throw _Exception(_("sqlite3 task queue is already closed"));
00348 }
00349 if (! onlyIfDirty || dirty)
00350 {
00351 taskQueue->enqueue(task);
00352 signal();
00353 }
00354 }
00355
00356 void Sqlite3Storage::shutdownDriver()
00357 {
00358 log_debug("start\n");
00359 AUTOLOCK(sqliteMutex);
00360 shutdownFlag = true;
00361 log_debug("signalling...\n");
00362 signal();
00363 AUTOUNLOCK();
00364 log_debug("waiting for thread\n");
00365 if (sqliteThread)
00366 pthread_join(sqliteThread, NULL);
00367 sqliteThread = 0;
00368 log_debug("end\n");
00369 }
00370
00371 void Sqlite3Storage::storeInternalSetting(String key, String value)
00372 {
00373 Ref<StringBuffer> q(new StringBuffer());
00374 *q << "INSERT OR REPLACE INTO " << QTB << INTERNAL_SETTINGS_TABLE << QTE << " (" << QTB << "key" << QTE << ", " << QTB << "value" << QTE << ") "
00375 "VALUES (" << quote(key) << ", "<< quote(value) << ") ";
00376 SQLStorage::exec(q);
00377 }
00378
00379 void Sqlite3Storage::_addToInsertBuffer(Ref<StringBuffer> query)
00380 {
00381 if (insertBuffer == nil)
00382 {
00383 insertBuffer = Ref<StringBuffer>(new StringBuffer());
00384 *insertBuffer << "BEGIN TRANSACTION;";
00385 }
00386
00387 *insertBuffer << query << ';';
00388 }
00389
00390 void Sqlite3Storage::_flushInsertBuffer()
00391 {
00392 if (insertBuffer == nil)
00393 return;
00394 *insertBuffer << "COMMIT;";
00395 SQLStorage::exec(insertBuffer);
00396 insertBuffer->clear();
00397 *insertBuffer << "BEGIN TRANSACTION;";
00398 }
00399
00400
00401
00402 SLTask::SLTask() : Object()
00403 {
00404 running = true;
00405 mutex = Ref<Mutex>(new Mutex());
00406 cond = Ref<Cond>(new Cond(mutex));
00407 error = nil;
00408 contamination = false;
00409 decontamination = false;
00410 }
00411 bool SLTask::is_running()
00412 {
00413 return running;
00414 }
00415
00416 void SLTask::sendSignal()
00417 {
00418 AUTOLOCK(mutex);
00419 running=false;
00420 cond->signal();
00421 }
00422
00423 void SLTask::sendSignal(String error)
00424 {
00425 this->error = error;
00426 sendSignal();
00427 }
00428
00429 void SLTask::waitForTask()
00430 {
00431 if (is_running())
00432 {
00433 AUTOLOCK(mutex);
00434 if (is_running())
00435 {
00436 cond->wait();
00437 }
00438 }
00439
00440 if (getError() != nil)
00441 {
00442 log_debug("%s\n", getError().c_str());
00443 throw _Exception(getError());
00444 }
00445 }
00446
00447 #ifdef AUTO_CREATE_DATABASE
00448
00449
00450 void SLInitTask::run(sqlite3 **db, Sqlite3Storage *sl)
00451 {
00452 String dbFilePath = ConfigManager::getInstance()->getOption(CFG_SERVER_STORAGE_SQLITE_DATABASE_FILE);
00453
00454 sqlite3_close(*db);
00455
00456 if (unlink(dbFilePath.c_str()) != 0)
00457 throw _StorageException(nil, _("error while autocreating sqlite3 database: could not unlink old database file: ") + mt_strerror(errno));
00458
00459 int res = sqlite3_open(dbFilePath.c_str(), db);
00460 if (res != SQLITE_OK)
00461 throw _StorageException(nil, _("error while autocreating sqlite3 database: could not create new database"));
00462
00463 unsigned char buf[SL3_CREATE_SQL_INFLATED_SIZE + 1];
00464 unsigned long uncompressed_size = SL3_CREATE_SQL_INFLATED_SIZE;
00465 int ret = uncompress(buf, &uncompressed_size, sqlite3_create_sql, SL3_CREATE_SQL_DEFLATED_SIZE);
00466 if (ret != Z_OK || uncompressed_size != SL3_CREATE_SQL_INFLATED_SIZE)
00467 throw _StorageException(nil, _("Error while uncompressing sqlite3 create sql. returned: ") + ret);
00468 buf[SL3_CREATE_SQL_INFLATED_SIZE] = '\0';
00469
00470 char *err = NULL;
00471 ret = sqlite3_exec(
00472 *db,
00473 (const char *)buf,
00474 NULL,
00475 NULL,
00476 &err
00477 );
00478 String error = nil;
00479 if (err != NULL)
00480 {
00481 error = err;
00482 sqlite3_free(err);
00483 }
00484 if(ret != SQLITE_OK)
00485 {
00486 throw _StorageException(nil, sl->getError((const char*)buf, error, *db));
00487 }
00488 contamination = true;
00489 }
00490
00491 #endif
00492
00493
00494
00495 SLSelectTask::SLSelectTask(const char *query) : SLTask()
00496 {
00497 this->query = query;
00498 }
00499
00500 void SLSelectTask::run(sqlite3 **db, Sqlite3Storage *sl)
00501 {
00502
00503 pres = Ref<Sqlite3Result>(new Sqlite3Result());
00504
00505 char *err;
00506 int ret = sqlite3_get_table(
00507 *db,
00508 query,
00509 &pres->table,
00510 &pres->nrow,
00511 &pres->ncolumn,
00512 &err
00513 );
00514 String error = nil;
00515 if (err != NULL)
00516 {
00517 error = err;
00518 sqlite3_free(err);
00519 }
00520 if(ret != SQLITE_OK)
00521 {
00522 throw _StorageException(nil, sl->getError(query, error, *db));
00523 }
00524
00525 pres->row = pres->table;
00526 pres->cur_row = 0;
00527 }
00528
00529
00530
00531 SLExecTask::SLExecTask(const char *query, bool getLastInsertId) : SLTask()
00532 {
00533 this->query = query;
00534 this->getLastInsertIdFlag = getLastInsertId;
00535 }
00536
00537 void SLExecTask::run(sqlite3 **db, Sqlite3Storage *sl)
00538 {
00539
00540 char *err;
00541 int res = sqlite3_exec(
00542 *db,
00543 query,
00544 NULL,
00545 NULL,
00546 &err
00547 );
00548 String error = nil;
00549 if (err != NULL)
00550 {
00551 error = err;
00552 sqlite3_free(err);
00553 }
00554 if(res != SQLITE_OK)
00555 {
00556 throw _StorageException(nil, sl->getError(query, error, *db));
00557 }
00558 if (getLastInsertIdFlag)
00559 lastInsertId = sqlite3_last_insert_rowid(*db);
00560 contamination = true;
00561 }
00562
00563
00564
00565 void SLBackupTask::run(sqlite3 **db, Sqlite3Storage *sl)
00566 {
00567
00568 String dbFilePath = ConfigManager::getInstance()->getOption(CFG_SERVER_STORAGE_SQLITE_DATABASE_FILE);
00569
00570 if (! restore)
00571 {
00572 try
00573 {
00574 copy_file(
00575 dbFilePath,
00576 dbFilePath + ".backup"
00577 );
00578 log_debug("sqlite3 backup successful\n");
00579 decontamination = true;
00580 }
00581 catch (Exception e)
00582 {
00583 log_error("error while making sqlite3 backup: %s\n", e.getMessage().c_str());
00584 }
00585 }
00586 else
00587 {
00588 log_info("trying to restore sqlite3 database from backup...\n");
00589 sqlite3_close(*db);
00590 try
00591 {
00592 copy_file(
00593 dbFilePath + ".backup",
00594 dbFilePath
00595 );
00596
00597 }
00598 catch (Exception e)
00599 {
00600 throw _StorageException(nil, _("error while restoring sqlite3 backup: ") + e.getMessage());
00601 }
00602 int res = sqlite3_open(dbFilePath.c_str(), db);
00603 if (res != SQLITE_OK)
00604 {
00605 throw _StorageException(nil, _("error while restoring sqlite3 backup: could not reopen sqlite3 database after restore"));
00606 }
00607 log_info("sqlite3 database successfully restored from backup.\n");
00608 }
00609
00610 }
00611
00612
00613
00614
00615 Sqlite3Result::Sqlite3Result() : SQLResult()
00616 {
00617 table = NULL;
00618 }
00619 Sqlite3Result::~Sqlite3Result()
00620 {
00621 if(table)
00622 {
00623 sqlite3_free_table(table);
00624 table = NULL;
00625 }
00626 }
00627 Ref<SQLRow> Sqlite3Result::nextRow()
00628 {
00629 if(nrow)
00630 {
00631 row += ncolumn;
00632 cur_row++;
00633 if (cur_row <= nrow)
00634 {
00635 Ref<Sqlite3Row> p (new Sqlite3Row(row, Ref<SQLResult>(this)));
00636 p->res = Ref<Sqlite3Result>(this);
00637 return RefCast(p, SQLRow);
00638 }
00639 else
00640 return nil;
00641 }
00642 return nil;
00643
00644 }
00645
00646
00647
00648 Sqlite3Row::Sqlite3Row(char **row, Ref<SQLResult> sqlResult) : SQLRow(sqlResult)
00649 {
00650 this->row = row;
00651 }
00652
00653
00654
00655 void Sqlite3BackupTimerSubscriber::timerNotify(Ref<Object> sqlite3storage)
00656 {
00657 Sqlite3Storage *storage = (Sqlite3Storage*)(sqlite3storage.getPtr());
00658
00659 Ref<SLBackupTask> btask (new SLBackupTask(false));
00660 storage->addTask(RefCast(btask, SLTask), true);
00661 }
00662
00663 #endif // HAVE_SQLITE3