00001 #ifndef GLIM_SQLITE_HPP_
00002 #define GLIM_SQLITE_HPP_
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 #include <stdexcept>
00028 #include <sqlite3.h>
00029 #include <pthread.h>
00030 #include <string.h>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <unistd.h>
00034 #include <errno.h>
00035 #include <stdint.h>
00036
00037 namespace glim {
00038
00039 class SqliteSession;
00040 class SqliteQuery;
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 class Sqlite {
00069
00070 Sqlite& operator = (const Sqlite& other) {return *this;}
00071
00072 Sqlite (const Sqlite& other) {}
00073 friend class SqliteSession;
00074 protected:
00075
00076
00077 std::string filename;
00078 ::sqlite3* handler;
00079 ::pthread_mutex_t mutex;
00080 public:
00081
00082 enum Flags {
00083
00084
00085
00086
00087
00088
00089 existing = 1
00090 };
00091
00092
00093
00094
00095
00096
00097 Sqlite (std::string filename, int flags = 0) {
00098 if (flags & existing) {
00099
00100 struct stat st; if (stat (filename.c_str(), &st))
00101 throw std::runtime_error(filename + ": " + strerror(errno));
00102 }
00103 ::pthread_mutex_init (&mutex, NULL);
00104 this->filename = filename;
00105 if (::sqlite3_open(filename.c_str(), &handler) != SQLITE_OK)
00106 throw std::runtime_error(std::string("sqlite3_open(") + filename + "): " + ::sqlite3_errmsg(handler));
00107 }
00108
00109
00110
00111
00112 ~Sqlite () {
00113 ::pthread_mutex_destroy (&mutex);
00114 if (::sqlite3_close(handler) != SQLITE_OK)
00115 throw std::runtime_error(std::string ("sqlite3_close(): ") + ::sqlite3_errmsg(handler));
00116 }
00117 };
00118
00119
00120
00121
00122
00123
00124
00125 class SqliteSession {
00126
00127 SqliteSession& operator = (const SqliteSession& other) {return *this;}
00128
00129 SqliteSession(SqliteSession& other) {}
00130 protected:
00131 Sqlite* db;
00132 public:
00133
00134
00135
00136
00137 SqliteSession (Sqlite* sqlite): db (sqlite) {
00138 int err = ::pthread_mutex_lock (&(db->mutex));
00139 if (err != 0) throw std::runtime_error(std::string ("error locking the mutex: ") + strerror(err));
00140 }
00141
00142
00143
00144
00145
00146 template <typename T>
00147 SqliteQuery query (T t);
00148
00149
00150 ~SqliteSession () {close();}
00151
00152
00153
00154
00155
00156
00157
00158 void close () {
00159 if (db == NULL) return;
00160 int err = ::pthread_mutex_unlock (&(db->mutex));
00161 db = NULL;
00162 if (err != 0) throw std::runtime_error(std::string ("error unlocking the mutex: ") + strerror(err));
00163 }
00164
00165 bool isClosed () const {
00166 return db == NULL;
00167 }
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178 operator ::sqlite3* () const {return db->handler;}
00179 };
00180
00181
00182
00183
00184 class SqliteQuery {
00185 protected:
00186 ::sqlite3_stmt* statement;
00187 SqliteSession* session;
00188 int bindCounter;
00189
00190 int mChanges;
00191 void prepare (SqliteSession* session, char const* query, int queryLength) {
00192 ::sqlite3* handler = *session;
00193 if (::sqlite3_prepare (handler, query, queryLength, &statement, NULL) != SQLITE_OK)
00194 throw std::runtime_error(std::string(query, queryLength) + ": " + ::sqlite3_errmsg(handler));
00195 }
00196 public:
00197
00198
00199
00200
00201 SqliteQuery (SqliteSession* session, char const* query, int queryLength)
00202 : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
00203 prepare (session, query, queryLength);
00204 }
00205
00206
00207
00208
00209 SqliteQuery (SqliteSession* session, std::pair<char const*, int> query)
00210 : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
00211 prepare (session, query.first, query.second);
00212 }
00213
00214
00215
00216
00217 SqliteQuery (SqliteSession* session, std::string query)
00218 : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
00219 prepare (session, query.c_str(), query.length());
00220 }
00221
00222
00223
00224
00225 ~SqliteQuery () {
00226 if (statement) ::sqlite3_finalize (statement);
00227 }
00228
00229
00230
00231 SqliteQuery& reset () {
00232 bindCounter = 0;
00233 mChanges = -1;
00234 ::sqlite3_reset (statement);
00235 return *this;
00236 }
00237
00238
00239 bool next () {return step();}
00240
00241
00242
00243
00244
00245 bool step () {
00246 if (mChanges >= 0) {mChanges = 0; return false;}
00247 int ret = ::sqlite3_step (statement);
00248 if (ret == SQLITE_ROW) return true;
00249 if (ret == SQLITE_DONE) {
00250 mChanges = ::sqlite3_changes (*session);
00251 return false;
00252 }
00253 throw std::runtime_error (std::string(::sqlite3_errmsg(*session)));
00254 }
00255
00256
00257
00258
00259
00260 SqliteQuery& qstep () {
00261 if (!step())
00262 throw std::runtime_error (std::string("qstep: no rows returned / affected"));
00263 return *this;
00264 }
00265
00266
00267
00268
00269
00270
00271
00272 int ustep () {
00273 int ret = ::sqlite3_step (statement);
00274 if (ret == SQLITE_DONE) {
00275 mChanges = ::sqlite3_changes (*session);
00276 return mChanges;
00277 }
00278 if (ret == SQLITE_ROW) return 0;
00279 throw std::runtime_error (std::string(::sqlite3_errmsg(*session)));
00280 }
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297 int changes () {return mChanges;}
00298
00299
00300
00301
00302
00303
00304 int intAt (int column) {
00305 return ::sqlite3_column_int (statement, --column);
00306 }
00307
00308
00309
00310
00311
00312
00313 int64_t int64at (int column) {
00314 return (int64_t) ::sqlite3_column_int64 (statement, --column);
00315 }
00316
00317
00318
00319
00320
00321
00322 double doubleAt (int column) {
00323 return ::sqlite3_column_double (statement, --column);
00324 }
00325
00326
00327
00328
00329
00330
00331 std::pair<char const*, int> charsAt (int column) {
00332 return std::pair<char const*, int> ((char const*) ::sqlite3_column_text (statement, column-1),
00333 ::sqlite3_column_bytes (statement, column-1));
00334 }
00335
00336
00337
00338
00339
00340 std::string stringAt (int column) {
00341 return std::string ((char const*) ::sqlite3_column_text (statement, column-1),
00342 ::sqlite3_column_bytes (statement, column-1));
00343 }
00344
00345
00346
00347
00348
00349
00350
00351 int typeAt (int column) {
00352 return ::sqlite3_column_type (statement, --column);
00353 }
00354
00355
00356
00357
00358 template<typename T>
00359 SqliteQuery& operator << (T value) {
00360 return bind (++bindCounter, value);
00361 }
00362
00363
00364
00365
00366
00367 template<typename T>
00368 SqliteQuery& bind (char const* name, T value) {
00369 int index = ::sqlite3_bind_parameter_index (statement, name);
00370 if (index == 0)
00371 throw std::runtime_error (std::string ("No such parameter in the query: ") + name);
00372 return bind (index, value);
00373 }
00374
00375
00376
00377
00378
00379 SqliteQuery& bind (int index, const char* text, int length, bool transient = false) {
00380 if (::sqlite3_bind_text (statement, index, text, length,
00381 transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
00382 throw std::runtime_error (std::string (::sqlite3_errmsg (*session)));
00383 return *this;
00384 }
00385
00386
00387
00388
00389 SqliteQuery& bind (int index, std::pair<const char*, int> text, bool transient = false) {
00390 if (::sqlite3_bind_text (statement, index, text.first, text.second,
00391 transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
00392 throw std::runtime_error (std::string (::sqlite3_errmsg (*session)));
00393 return *this;
00394 }
00395
00396
00397
00398
00399 SqliteQuery& bind (int index, std::string text, bool transient = true) {
00400 if (::sqlite3_bind_text (statement, index, text.c_str(), text.length(),
00401 transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
00402 throw std::runtime_error (std::string (::sqlite3_errmsg (*session)));
00403 return *this;
00404 }
00405
00406
00407
00408 SqliteQuery& bind (int index, int value) {
00409 if (::sqlite3_bind_int (statement, index, value) != SQLITE_OK)
00410 throw std::runtime_error (std::string (::sqlite3_errmsg (*session)));
00411 return *this;
00412 }
00413
00414
00415
00416 SqliteQuery& bind (int index, long long int value) {
00417 if (::sqlite3_bind_int (statement, index, value) != SQLITE_OK)
00418 throw std::runtime_error (std::string (::sqlite3_errmsg (*session)));
00419 return *this;
00420 }
00421 };
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432 class SqliteParQuery: public SqliteQuery {
00433 protected:
00434 char const* query;
00435 int queryLength;
00436 int repeat;
00437 int wait;
00438 public:
00439
00440
00441
00442
00443
00444
00445 SqliteParQuery (SqliteSession* session, char const* query, int queryLength, int repeat = 90, int wait = 20)
00446 : SqliteQuery (session, query, queryLength) {
00447 this->query = query;
00448 this->queryLength = queryLength;
00449 this->repeat = repeat;
00450 this->wait = wait;
00451 }
00452
00453
00454
00455
00456
00457
00458
00459 SqliteParQuery (SqliteSession* session, std::pair<char const*, int> query, int repeat = 90, int wait = 20)
00460 : SqliteQuery (session, query) {
00461 this->query = query.first;
00462 this->queryLength = query.second;
00463 this->repeat = repeat;
00464 this->wait = wait;
00465 }
00466
00467 bool next () {return step();}
00468 bool step () {
00469 if (mChanges >= 0) {mChanges = 0; return false;}
00470 repeat:
00471 int ret = ::sqlite3_step (statement);
00472 if (ret == SQLITE_ROW) return true;
00473 if (ret == SQLITE_DONE) {
00474 mChanges = ::sqlite3_changes (*session);
00475 return false;
00476 }
00477 if (ret == SQLITE_SCHEMA) {
00478 ::sqlite3_stmt* old = statement;
00479 prepare (session, query, queryLength);
00480 ::sqlite3_transfer_bindings(old, statement);
00481 ::sqlite3_finalize (old);
00482 goto repeat;
00483 }
00484 if (ret == SQLITE_BUSY) for (int repeat = this->repeat; ret == SQLITE_BUSY;) {
00485
00486
00487 ::sqlite3_sleep (wait);
00488 ret = ::sqlite3_step (statement);
00489 }
00490 throw std::runtime_error (std::string(query, queryLength) + ::sqlite3_errmsg(*session));
00491 }
00492 };
00493
00494 template <typename T>
00495 SqliteQuery SqliteSession::query (T t) {
00496 return SqliteQuery (this, t);
00497 }
00498
00499 };
00500
00501 #endif // GLIM_SQLITE_HPP_