00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <set>
00021 #include <boost/filesystem/operations.hpp>
00022 #include <boost/regex.hpp>
00023 #include <pion/platform/ConfigManager.hpp>
00024 #include <pion/platform/DatabaseManager.hpp>
00025 #include "SQLiteDatabase.hpp"
00026
00027 using namespace pion::platform;
00028
00029
00030 namespace pion {
00031 namespace plugins {
00032
00033
00034
00035
00036 const std::string SQLiteDatabase::BACKUP_FILE_EXTENSION = ".bak";
00037 const std::string SQLiteDatabase::FILENAME_ELEMENT_NAME = "Filename";
00038
00039
00040
00041
00042 void SQLiteDatabase::setConfig(const Vocabulary& v, const xmlNodePtr config_ptr)
00043 {
00044 Database::setConfig(v, config_ptr);
00045
00046 readConfig(config_ptr, "SQLite");
00047
00048
00049 if (! ConfigManager::getConfigOption(FILENAME_ELEMENT_NAME, m_database_name, config_ptr))
00050 throw EmptyFilenameException(getId());
00051
00052
00053 m_database_name = getDatabaseManager().resolveRelativeDataPath(m_database_name);
00054 }
00055
00056 DatabasePtr SQLiteDatabase::clone(void) const
00057 {
00058 SQLiteDatabase *db_ptr(new SQLiteDatabase());
00059 db_ptr->copyDatabase(*this);
00060 db_ptr->m_database_name = m_database_name;
00061 return DatabasePtr(db_ptr);
00062 }
00063
00064 void SQLiteDatabase::open(unsigned partition)
00065 {
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076 m_partition = partition;
00077
00078 std::string dbname = dbPartition(m_database_name, partition);
00079
00080
00081 if (sqlite3_enable_shared_cache(1) != SQLITE_OK)
00082 throw SQLiteAPIException(getSQLiteError());
00083
00084
00085
00086 if (sqlite3_open_v2(dbname.c_str(), &m_sqlite_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, NULL) != SQLITE_OK) {
00087
00088 if (m_sqlite_db != NULL) {
00089 sqlite3_close(m_sqlite_db);
00090 m_sqlite_db = NULL;
00091 }
00092 throw OpenDatabaseException(dbname);
00093 }
00094
00095
00096 sqlite3_busy_timeout(m_sqlite_db, 10000);
00097
00098
00099 boost::uint64_t page_size = 1024;
00100 boost::uint64_t cache_size = 2000;
00101 boost::regex regex_cache_size("pragma\\s+cache_size\\s+=\\s*([0-9]+)", boost::regex::extended | boost::regex::icase);
00102 boost::regex regex_default_cache_size("pragma\\s+default_cache_size\\s+=\\s*([0-9]+)", boost::regex::extended | boost::regex::icase);
00103 boost::regex regex_page_size("pragma\\s+page_size\\s+=\\s*([0-9]+)", boost::regex::extended | boost::regex::icase);
00104
00105 for (unsigned i = 0; i < m_pre_sql.size(); i++) {
00106 if (sqlite3_exec(m_sqlite_db, m_pre_sql[i].c_str(), NULL, NULL, &m_error_ptr) != SQLITE_OK)
00107 throw SQLiteAPIException(getSQLiteError());
00108 std::string s;
00109 if (regex_search(m_pre_sql[i], regex_cache_size))
00110 s = regex_replace(m_pre_sql[i], regex_cache_size, "$1", boost::format_all | boost::format_first_only | boost::format_no_copy);
00111 if (regex_search(m_pre_sql[i], regex_default_cache_size))
00112 s = regex_replace(m_pre_sql[i], regex_default_cache_size, "$1", boost::format_all | boost::format_first_only | boost::format_no_copy);
00113 if (!s.empty())
00114 cache_size = boost::lexical_cast<boost::uint64_t>(s);
00115 if (regex_search(m_pre_sql[i], regex_page_size)) {
00116 s = regex_replace(m_pre_sql[i], regex_page_size, "$1", boost::format_all | boost::format_first_only | boost::format_no_copy);
00117 if (!s.empty())
00118 page_size = boost::lexical_cast<boost::uint64_t>(s);
00119 }
00120 }
00121 m_cache_size = page_size * cache_size;
00122 }
00123
00124 void SQLiteDatabase::close(void)
00125 {
00126 if (m_sqlite_db != NULL) {
00127
00128 sqlite3_stmt *pStmt;
00129 while ((pStmt = sqlite3_next_stmt(m_sqlite_db, 0)) != 0)
00130 sqlite3_finalize(pStmt);
00131 sqlite3_close(m_sqlite_db);
00132 }
00133 m_sqlite_db = NULL;
00134 }
00135
00136 void SQLiteDatabase::runQuery(const std::string& sql_query, const boost::regex& suppress)
00137 {
00138
00139 PION_ASSERT(is_open());
00140 PION_ASSERT(! sql_query.empty());
00141
00142
00143 if (sqlite3_exec(m_sqlite_db, sql_query.c_str(), NULL, NULL, &m_error_ptr) != SQLITE_OK && !suppress.empty())
00144 throw SQLiteAPIException(getSQLiteError());
00145 }
00146
00147 QueryPtr SQLiteDatabase::addQuery(QueryID query_id,
00148 const std::string& sql_query)
00149 {
00150
00151 PION_ASSERT(is_open());
00152 PION_ASSERT(! query_id.empty());
00153 PION_ASSERT(! sql_query.empty());
00154
00155
00156 QueryPtr query_ptr(new SQLiteQuery(sql_query, m_sqlite_db));
00157
00158
00159 m_query_map.insert(std::make_pair(query_id, query_ptr));
00160
00161
00162 return query_ptr;
00163 }
00164
00165 void SQLiteDatabase::createTable(const Query::FieldMap& field_map,
00166 std::string& table_name,
00167 const Query::IndexMap& index_map,
00168 unsigned partition)
00169 {
00170 PION_ASSERT(is_open());
00171
00172 if (partition) {
00173 char buff[10];
00174 sprintf(buff, "_%03u", partition);
00175 table_name += buff;
00176 }
00177 PION_LOG_DEBUG(m_logger, "createTable " + table_name);
00178
00179 bool DidItExist = boost::filesystem::exists(dbPartition(m_database_name, partition));
00180
00181 PION_LOG_DEBUG(m_logger, "createTable/exists (" << DidItExist << ")" << dbPartition(m_database_name, partition));
00182
00183
00184 std::string create_table_sql = m_create_log;
00185 stringSubstitutes(create_table_sql, field_map, table_name);
00186
00187
00188 runQuery(create_table_sql, m_create_log_attr);
00189
00190 std::string Sql = "PRAGMA table_info(" + table_name + ')';
00191 try {
00192 sqlite3_stmt *pStmt;
00193 if (sqlite3_prepare_v2(m_sqlite_db, Sql.c_str(), Sql.size(), &pStmt, NULL) == SQLITE_OK) {
00194
00195
00196 std::set<std::string> columns_found;
00197 while (sqlite3_step(pStmt) == SQLITE_ROW) {
00198
00199
00200 const char *col = (const char *)sqlite3_column_text(pStmt, 1);
00201 if (col && *col) {
00202 columns_found.insert(col);
00203 PION_LOG_DEBUG(m_logger, "createTable/schemaCheck, found: " << col);
00204 }
00205 }
00206 sqlite3_finalize(pStmt);
00207
00208
00209
00210 for (unsigned p = 0; p < field_map.size(); p++) {
00211
00212
00213 if (columns_found.find(field_map[p].first) == columns_found.end()) {
00214
00215
00216 Sql = "ALTER TABLE " + table_name + " ADD COLUMN " + field_map[p].first + ' ' +
00217 m_sql_affinity[field_map[p].second.term_type];
00218 PION_LOG_DEBUG(m_logger, "createTable, add column " + Sql);
00219
00220
00221
00222
00223 sqlite3_exec(m_sqlite_db, Sql.c_str(), NULL, NULL, &m_error_ptr);
00224 }
00225 }
00226 } else
00227 sqlite3_finalize(pStmt);
00228 } catch (...) {
00229 }
00230
00231
00232 bool RowsInTable = true;
00233 try {
00234 Sql = "SELECT * FROM " + table_name + " LIMIT 1";
00235 sqlite3_stmt *pStmt;
00236 if (sqlite3_prepare_v2(m_sqlite_db, Sql.c_str(), Sql.size(), &pStmt, NULL) == SQLITE_OK)
00237 RowsInTable =(sqlite3_step(pStmt) == SQLITE_ROW);
00238 sqlite3_finalize(pStmt);
00239 } catch (...) {
00240 }
00241
00242
00243
00244
00245
00246
00247 if (!RowsInTable)
00248 for (unsigned i = 0; i < index_map.size(); i++) {
00249 const std::string idxname = table_name + "_" + field_map[i].first + "_idx";
00250 if (index_map[i] == "false" || index_map[i].empty())
00251 Sql = "DROP INDEX IF EXISTS " + idxname;
00252 else if (index_map[i] == "true")
00253 Sql = "CREATE INDEX IF NOT EXISTS " + idxname + " ON " + table_name + " ( " + field_map[i].first + " )";
00254 else if (index_map[i] == "unique")
00255 Sql = "CREATE UNIQUE INDEX IF NOT EXISTS " + idxname + " ON " + table_name + " ( " + field_map[i].first + " )";
00256 else
00257 Sql = "CREATE INDEX IF NOT EXISTS " + idxname + " ON " + table_name + " ( " + index_map[i] + " )";
00258
00259 PION_LOG_DEBUG(m_logger, "createTable/index: " + Sql);
00260 if (sqlite3_exec(m_sqlite_db, Sql.c_str(), NULL, NULL, &m_error_ptr) != SQLITE_OK)
00261 throw SQLiteAPIException(getSQLiteError());
00262 }
00263 }
00264
00265 void SQLiteDatabase::dropTable(std::string& table_name, unsigned partition)
00266 {
00267 m_partition = partition;
00268 if (m_sqlite_db) {
00269 close();
00270
00271 if (unlink(dbPartition(m_database_name, partition).c_str()))
00272 throw SQLiteAPIException(strerror(errno) + dbPartition(m_database_name, partition));
00273
00274 open(partition);
00275 } else {
00276 if (unlink(dbPartition(m_database_name, partition).c_str()))
00277
00278 ;
00279 }
00280 }
00281
00282
00283 bool SQLiteDatabase::tableExists(std::string& table_name, unsigned partition)
00284 {
00285 m_partition = partition;
00286 if (m_sqlite_db)
00287 return true;
00288 else
00289 return boost::filesystem::exists(dbPartition(m_database_name, partition));
00290 }
00291
00292 QueryPtr SQLiteDatabase::prepareInsertQuery(const Query::FieldMap& field_map,
00293 const std::string& table_name)
00294 {
00295 PION_ASSERT(is_open());
00296
00297
00298 QueryMap::const_iterator query_it = m_query_map.find(INSERT_QUERY_ID);
00299 if (query_it != m_query_map.end())
00300 return query_it->second;
00301
00302
00303 std::string insert_sql = m_insert_log;
00304 stringSubstitutes(insert_sql, field_map, table_name);
00305
00306
00307 return addQuery(Database::INSERT_QUERY_ID, insert_sql);
00308 }
00309
00310 QueryPtr SQLiteDatabase::prepareInsertIgnoreQuery(const Query::FieldMap& field_map,
00311 const std::string& table_name)
00312 {
00313 PION_ASSERT(is_open());
00314
00315
00316 QueryMap::const_iterator query_it = m_query_map.find(INSERT_IGNORE_QUERY_ID);
00317 if (query_it != m_query_map.end())
00318 return query_it->second;
00319
00320
00321 std::string insert_sql = m_insert_ignore;
00322 stringSubstitutes(insert_sql, field_map, table_name);
00323
00324
00325 return addQuery(Database::INSERT_IGNORE_QUERY_ID, insert_sql);
00326 }
00327
00328 QueryPtr SQLiteDatabase::getBeginTransactionQuery(void)
00329 {
00330 PION_ASSERT(is_open());
00331 QueryMap::const_iterator i = m_query_map.find(BEGIN_QUERY_ID);
00332 if (i == m_query_map.end())
00333 return addQuery(BEGIN_QUERY_ID, m_begin_insert);
00334 return i->second;
00335 }
00336
00337 QueryPtr SQLiteDatabase::getCommitTransactionQuery(void)
00338 {
00339 PION_ASSERT(is_open());
00340 QueryMap::const_iterator i = m_query_map.find(COMMIT_QUERY_ID);
00341 if (i == m_query_map.end())
00342 return addQuery(COMMIT_QUERY_ID, m_commit_insert);
00343 return i->second;
00344 }
00345
00346 QueryPtr SQLiteDatabase::prepareFullQuery(const std::string& query, const boost::regex& suppress)
00347 {
00348 PION_ASSERT(is_open());
00349
00350 QueryPtr query_ptr(new SQLiteQuery(query, m_sqlite_db));
00351 return query_ptr;
00352 }
00353
00354 bool SQLiteDatabase::SQLiteQuery::runFullQuery(const pion::platform::Query::FieldMap& ins, const pion::platform::EventPtr& src,
00355 const pion::platform::Query::FieldMap& outs, pion::platform::EventPtr& dest, unsigned int limit, const boost::regex& suppress)
00356 {
00357 bool changes = false;
00358 sqlite3_reset(m_sqlite_stmt);
00359 bindEvent(ins, *src);
00360 while (sqlite3_step(m_sqlite_stmt) == SQLITE_ROW) {
00361 fetchEvent(outs, dest);
00362 changes = true;
00363 if (!--limit) return changes;
00364 }
00365 return changes;
00366 }
00367
00368 bool SQLiteDatabase::SQLiteQuery::runFullGetMore(const pion::platform::Query::FieldMap& outs, pion::platform::EventPtr& dest,
00369 unsigned int limit)
00370 {
00371 bool changes = false;
00372 while (sqlite3_step(m_sqlite_stmt) == SQLITE_ROW) {
00373 fetchEvent(outs, dest);
00374 changes = true;
00375 if (!--limit) return changes;
00376 }
00377 return changes;
00378 }
00379
00380
00381
00382
00383
00384 SQLiteDatabase::SQLiteQuery::SQLiteQuery(const std::string& sql_query, sqlite3 *db_ptr)
00385 : Query(sql_query), m_sqlite_db(db_ptr), m_sqlite_stmt(NULL)
00386 {
00387 PION_ASSERT(db_ptr != NULL);
00388 if (sqlite3_prepare_v2(m_sqlite_db, sql_query.c_str(), sql_query.size(),
00389 &m_sqlite_stmt, NULL) != SQLITE_OK)
00390 SQLiteDatabase::throwAPIException(m_sqlite_db);
00391 PION_ASSERT(m_sqlite_stmt != NULL);
00392 }
00393
00394 bool SQLiteDatabase::SQLiteQuery::run(void)
00395 {
00396
00397 bool row_available = false;
00398 switch (sqlite3_step(m_sqlite_stmt)) {
00399 case SQLITE_LOCKED:
00400 case SQLITE_BUSY:
00401 throw Database::DatabaseBusyException();
00402 break;
00403 case SQLITE_ROW:
00404
00405 row_available = true;
00406 break;
00407 case SQLITE_DONE:
00408
00409 row_available = false;
00410 break;
00411 default:
00412 SQLiteDatabase::throwAPIException(m_sqlite_db);
00413 break;
00414 }
00415 return row_available;
00416 }
00417
00418
00419 bool SQLiteDatabase::SQLiteQuery::fetchRow(const FieldMap& field_map, EventPtr e)
00420 {
00421 bool row_available = false;
00422 switch (sqlite3_step(m_sqlite_stmt)) {
00423 case SQLITE_BUSY:
00424 throw SQLiteDatabase::DatabaseBusyException();
00425 break;
00426 case SQLITE_ROW:
00427
00428 fetchEvent(field_map, e);
00429 row_available = true;
00430 break;
00431 case SQLITE_DONE:
00432
00433
00434 break;
00435 default:
00436 SQLiteDatabase::throwAPIException(m_sqlite_db);
00437 break;
00438 }
00439 return row_available;
00440 }
00441
00442
00443 }
00444 }
00445
00446
00448 extern "C" PION_PLUGIN_API pion::platform::Database *pion_create_SQLiteDatabase(void) {
00449 return new pion::plugins::SQLiteDatabase();
00450 }
00451
00453 extern "C" PION_PLUGIN_API void pion_destroy_SQLiteDatabase(pion::plugins::SQLiteDatabase *database_ptr) {
00454 delete database_ptr;
00455 }