sqlitepp.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /*
00003   Phrasehunter - index and query text corpora
00004   Copyright (C) 2006  Torsten Marek (shlomme@gmx.de) &
00005   Armin Schmidt (armin.sch@gmail.com)
00006   
00007   This program is free software; you can redistribute it and/or
00008   modify it under the terms of the GNU General Public License
00009   as published by the Free Software Foundation; either version 2
00010   of the License, or (at your option) any later version.
00011   
00012   This program is distributed in the hope that it will be useful,
00013   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015   GNU General Public License for more details.
00016   
00017   You should have received a copy of the GNU General Public License
00018   along with this program; if not, write to the Free Software
00019   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
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    \brief Class for iterating over the result of a database query.
00051 */
00052 class ResultIterator
00053 {
00054 private:
00055     sqlite3_stmt* m_stmt;
00056     int state;
00057     
00058 public:
00059     /**
00060        \brief Constructor.
00061        \param stmt The Statement containing the sql query.
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     //! Returns true if there are more results. False otherwise.
00073     bool hasMoreRows()
00074     {
00075         return state == SQLITE_ROW;
00076     }
00077     //! Steps to the result.
00078     void next() {
00079         state = sqlite3_step(m_stmt);
00080         p_assert(state == SQLITE_ROW || state == SQLITE_DONE, "error in statement");
00081     }
00082     /**
00083        \brief Get the current result.
00084        template parameter T needs to be of one of the following types: int, unsigned,
00085        const char*, Blob, std::string, PhraseHunter::UnicodePtr.
00086        \param column The column in the result table.
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 //! Class that contains a statement for querying the sqlite db.
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     // sorry, i'm retarded...
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     //! Bind an argument to a Statement.
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     //! Bind an argument to a Statement.
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     //! Bind an argument to a Statement.
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     //! Bind an argument to a Statement.
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     //! Bind an argument to a Statement.
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     //! Returns a ResultIterator to be used to iterate over the result of the db query.
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     //! Reset this Statement. Use this before binding other arguments to this Statement.
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 //! Connect to and query sqlite database
00261 class SqliteDB
00262 {
00263 private:
00264     SqliteDB(const SqliteDB& db);
00265     SqliteDB& operator=(const SqliteDB& db);
00266 
00267 public:
00268     /**
00269        \brief Constructor.
00270        \param filename The path to the sqlite3 database
00271     */
00272     SqliteDB(const std::string& filename);
00273 
00274     /**
00275        \brief Use this factory function to initialze a Statement.
00276        \param sql A string containing a valid, well-formed sql query.
00277     */
00278     Statement::Pointer statement(const char* sql) 
00279     {
00280         return Statement::Pointer(new Statement(sql, m_dbhandle));
00281     }
00282 
00283     /**
00284        \brief Use this factory function to initialze a cached Statement.
00285        \param sql A string containing a valid, well-formed sql query.
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

Generated on Thu Dec 21 16:14:40 2006 for The Phrasehunter by  doxygen 1.5.1