00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifndef SQLITEPP_H
00023 #define SQLITEPP_H SQLITEPP_H
00024
00025 #include <iostream>
00026 #include <string>
00027 #include <cassert>
00028 #include <sqlite3.h>
00029 #include <boost/preprocessor/arithmetic/inc.hpp>
00030 #include <boost/preprocessor/punctuation/comma_if.hpp>
00031 #include <boost/preprocessor/repetition.hpp>
00032 #include <boost/static_assert.hpp>
00033
00034 #include <tr1/unordered_map>
00035 #include <boost/shared_ptr.hpp>
00036
00037 #include "support/debug.h"
00038 #include "support/unicodehelpers.h"
00039
00040
00041
00042 namespace SQLitePP {
00043
00044 typedef std::pair<const void*, size_t> Blob;
00045
00046 void showSqlite3Error(const char* infunction, sqlite3* db);
00047 sqlite3_stmt* prepareStatement(const char *sqlcode, sqlite3* db);
00048
00049
00050
00051
00052 class ResultIterator
00053 {
00054 private:
00055 sqlite3_stmt* m_stmt;
00056 int state;
00057
00058 public:
00059
00060
00061
00062
00063 ResultIterator(sqlite3_stmt* stmt): m_stmt(stmt), state(0)
00064 {
00065 next();
00066 }
00067
00068 ~ResultIterator()
00069 {
00070 sqlite3_reset(m_stmt);
00071 }
00072
00073 bool hasMoreRows()
00074 {
00075 return state == SQLITE_ROW;
00076 }
00077
00078 void next() {
00079 state = sqlite3_step(m_stmt);
00080 p_assert(state == SQLITE_ROW || state == SQLITE_DONE, "error in statement");
00081 }
00082
00083
00084
00085
00086
00087
00088 template<typename T>
00089 T get(int column)
00090 {
00091 BOOST_STATIC_ASSERT(sizeof(T) == 0);
00092 }
00093 };
00094
00095 template<>
00096 int ResultIterator::get<int>(int column);
00097
00098 template<>
00099 unsigned int ResultIterator::get<unsigned int>(int column);
00100
00101 template<>
00102 const char* ResultIterator::get<const char*>(int column);
00103
00104 template<>
00105 Blob ResultIterator::get<Blob>(int column) ;
00106
00107 template<>
00108 std::string ResultIterator::get<std::string>(int column);
00109
00110 template<>
00111 schma::UnicodePtr ResultIterator::get<schma::UnicodePtr>(int column);
00112
00113
00114 class SqliteDB;
00115
00116
00117 class Statement
00118 {
00119 sqlite3_stmt* m_stmt;
00120 sqlite3* m_dbhandle;
00121 int m_argpos;
00122 bool m_finalize;
00123
00124 friend class SqliteDB;
00125
00126 inline Statement(const char* sql, sqlite3* db)
00127 : m_stmt(prepareStatement(sql, db)), m_dbhandle(db),
00128 m_argpos(0), m_finalize(true)
00129 { }
00130
00131 inline Statement(sqlite3_stmt* stmt, sqlite3* db)
00132 : m_stmt(stmt), m_dbhandle(db),
00133 m_argpos(0), m_finalize(false)
00134 { }
00135
00136 public:
00137 typedef boost::shared_ptr<Statement> Pointer;
00138
00139
00140
00141 #ifndef MAX_ARGUMENTS
00142 #define MAX_ARGUMENTS 9
00143 #endif
00144
00145 #define BINDARG(Z, N, _) \
00146 bindArg(BOOST_PP_CAT(a, N));
00147
00148 #define BINDARGS(Z, N, _) \
00149 template<BOOST_PP_ENUM_PARAMS(BOOST_PP_INC(N), typename T)> \
00150 inline Statement& bindArgs(BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_INC(N), T, a)) \
00151 { \
00152 BOOST_PP_REPEAT(BOOST_PP_INC(N), BINDARG, _) \
00153 return *this; \
00154 }
00155
00156 BOOST_PP_REPEAT(BOOST_PP_INC(MAX_ARGUMENTS), BINDARGS, _)
00157
00158 #undef BINDARGS
00159 #undef BINDARG
00160
00161 ~Statement()
00162 {
00163 if(m_finalize) {
00164 sqlite3_finalize(m_stmt);
00165 } else {
00166 reset();
00167 }
00168 }
00169
00170
00171 inline Statement& bindArg(const Blob& argument, int pos = 0)
00172 {
00173 if(pos == 0) {
00174 pos = ++m_argpos;
00175 }
00176 sqlite3_bind_blob(m_stmt, pos, argument.first, argument.second, SQLITE_STATIC);
00177 return *this;
00178 }
00179
00180 inline Statement& bindArg(const std::string& argument, int pos = 0)
00181 {
00182 if(pos == 0) {
00183 pos = ++m_argpos;
00184 }
00185 sqlite3_bind_text(m_stmt, pos, argument.c_str(), argument.length(), SQLITE_TRANSIENT);
00186 return *this;
00187 }
00188
00189 inline Statement& bindArg(const char* argument, int pos = 0)
00190 {
00191 if(pos == 0) {
00192 pos = ++m_argpos;
00193 }
00194 sqlite3_bind_text(m_stmt, pos, argument, -1, SQLITE_TRANSIENT);
00195 return *this;
00196 }
00197
00198 inline Statement& bindArg(int argument, unsigned int pos = 0)
00199 {
00200 if(pos == 0) {
00201 pos = ++m_argpos;
00202 }
00203 sqlite3_bind_int(m_stmt, pos, argument);
00204 return *this;
00205 }
00206
00207 inline Statement& bindArg(schma::UnicodePtr argument, int pos = 0)
00208 {
00209 if(pos == 0) {
00210 pos = ++m_argpos;
00211 }
00212 sqlite3_bind_text(m_stmt, pos, schma::toCharArray(argument).get(), argument->length(), SQLITE_TRANSIENT);
00213 return *this;
00214 }
00215 inline Statement& exec()
00216 {
00217 int rcode = sqlite3_step(m_stmt);
00218 if(rcode != SQLITE_DONE) {
00219 showSqlite3Error(__PRETTY_FUNCTION__, m_dbhandle);
00220 }
00221 reset();
00222 return *this;
00223 }
00224
00225
00226 inline ResultIterator query()
00227 {
00228 m_argpos = 0;
00229 return ResultIterator(m_stmt);
00230 }
00231
00232 template<typename T>
00233 inline T queryScalar()
00234 {
00235 ResultIterator ri = query();
00236
00237 p_assert(ri.hasMoreRows(), "error");
00238
00239 return ri.get<T>(0);
00240 }
00241
00242
00243 inline void reset()
00244 {
00245 m_argpos = 0;
00246 sqlite3_reset(m_stmt);
00247 }
00248
00249 inline int lastInsertId()
00250 {
00251 return sqlite3_last_insert_rowid(m_dbhandle);
00252 }
00253
00254 inline int affectedRows()
00255 {
00256 return sqlite3_changes(m_dbhandle);
00257 }
00258 };
00259
00260
00261 class SqliteDB
00262 {
00263 private:
00264 SqliteDB(const SqliteDB& db);
00265 SqliteDB& operator=(const SqliteDB& db);
00266
00267 public:
00268
00269
00270
00271
00272 SqliteDB(const std::string& filename);
00273
00274
00275
00276
00277
00278 Statement::Pointer statement(const char* sql)
00279 {
00280 return Statement::Pointer(new Statement(sql, m_dbhandle));
00281 }
00282
00283
00284
00285
00286
00287 Statement::Pointer cachedStatement(const char* sql)
00288 {
00289 CacheMap::iterator it = m_cached.find(sql);
00290 if(it != m_cached.end()) {
00291 return Statement::Pointer(new Statement(it->second, m_dbhandle));
00292 } else {
00293 sqlite3_stmt* stmt =
00294 prepareStatement(sql, m_dbhandle);
00295 m_cached[sql] = stmt;
00296 return Statement::Pointer(new Statement(stmt, m_dbhandle));
00297 }
00298 }
00299
00300 void begin()
00301 {
00302 cachedStatement("BEGIN")->exec();
00303 }
00304
00305 void commit()
00306 {
00307 cachedStatement("COMMIT")->exec();
00308 }
00309
00310 void rollback()
00311 {
00312 cachedStatement("ROLLBACK")->exec();
00313 }
00314
00315 ~SqliteDB()
00316 {
00317 sqlite3_close(m_dbhandle);
00318 for(CacheMap::iterator it = m_cached.begin();
00319 it != m_cached.end();
00320 ++it) {
00321 sqlite3_finalize(it->second);
00322 }
00323 }
00324
00325 private:
00326 sqlite3* m_dbhandle;
00327
00328 typedef std::tr1::unordered_map<const char*, sqlite3_stmt*> CacheMap;
00329 CacheMap m_cached;
00330
00331 };
00332
00333
00334 }
00335
00336 #endif //SQLITEPP_H