Savarese Software Research Corporation

Database.h

Go to the documentation of this file.
00001 /*
00002  * Copyright 2006-2009 Savarese Software Research Corporation
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.savarese.com/software/ApacheLicense-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00022 #ifndef __SSRC_WSPR_DATABASE_DATABASE_H
00023 #define __SSRC_WSPR_DATABASE_DATABASE_H
00024 
00025 #include <ssrc/wispers/database/types.h>
00026 #include <sstream>
00027 #include <cstring>
00028 
00029 // TODO: Add support for handling SQLITE_BUSY.  Currently we throw!
00030 __BEGIN_NS_SSRC_WSPR_DATABASE
00031 
00032 struct QueryResult {
00033   unsigned int changes;
00034   result_set_ptr result_set;
00035 
00036   explicit QueryResult(const unsigned int changes = 0) :
00037     changes(changes), result_set()
00038   { }
00039 
00040   explicit QueryResult(const result_set_ptr & result_set) :
00041     changes(0), result_set(result_set)
00042   { }
00043 };
00044 
00045 // Forward declaration;
00046 
00047 class PreparedStatement {
00048   friend class Database;
00049 
00050   sqlite3_stmt_ptr _statement;
00051 
00052   PreparedStatement(sqlite3_ptr & db, const string & statement) :
00053     _statement(detail::sqlite3_stmt_initializer(db, statement),
00054                detail::sqlite3_stmt_init())
00055   {
00056     if(_statement.init_error())
00057       throw DatabaseException(_statement);
00058   }
00059 
00060 public:
00061 
00062   enum BindPolicy { BindTransient, BindStatic };
00063 
00064   bool expired() {
00065     return SQLite3::sqlite3_expired(_statement.get());
00066   }
00067 
00068   unsigned int count_parameters() {
00069     return SQLite3::sqlite3_bind_parameter_count(_statement.get());
00070   }
00071 
00072   PreparedStatement & bind(const unsigned int index, const double value)
00073     SSRC_DECL_THROW(DatabaseException)
00074   {
00075     if(SQLite3::sqlite3_bind_double(_statement.get(), index, value)
00076        != SQLITE_OK)
00077       throw DatabaseException(_statement);
00078     return *this;
00079   }
00080 
00081   PreparedStatement & bind(const unsigned int index, const int value)
00082     SSRC_DECL_THROW(DatabaseException)
00083   {
00084     if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
00085       throw DatabaseException(_statement);
00086     return *this;
00087   }
00088 
00089   PreparedStatement & bind(const unsigned int index, const unsigned int value)
00090     SSRC_DECL_THROW(DatabaseException)
00091   {
00092     return bind(index, static_cast<int>(value));
00093   }
00094 
00095   PreparedStatement & bind(const unsigned int index, const std::int16_t value)
00096     SSRC_DECL_THROW(DatabaseException)
00097   {
00098     return bind(index, static_cast<int>(value));
00099   }
00100 
00101   PreparedStatement & bind(const unsigned int index, const std::uint16_t value)
00102     SSRC_DECL_THROW(DatabaseException)
00103   {
00104     return bind(index, static_cast<int>(value));
00105   }
00106 
00107   PreparedStatement & bind(const unsigned int index, const bool value)
00108     SSRC_DECL_THROW(DatabaseException)
00109   {
00110     if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
00111       throw DatabaseException(_statement);
00112     return *this;
00113   }
00114 
00115   PreparedStatement & bind(const unsigned int index, const std::int64_t value)
00116     SSRC_DECL_THROW(DatabaseException)
00117   {
00118     if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
00119        != SQLITE_OK)
00120       throw DatabaseException(_statement);
00121     return *this;
00122   }
00123 
00124   PreparedStatement & bind(const unsigned int index, const std::uint64_t value)
00125     SSRC_DECL_THROW(DatabaseException)
00126   {
00127     return bind(index, static_cast<std::int64_t>(value));
00128   }
00129 
00130   PreparedStatement & bind(const unsigned int index, const long value)
00131     SSRC_DECL_THROW(DatabaseException)
00132   {
00133     if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
00134        != SQLITE_OK)
00135       throw DatabaseException(_statement);
00136     return *this;
00137   }
00138 
00139   PreparedStatement & bind(const unsigned int index, const unsigned long value)
00140     SSRC_DECL_THROW(DatabaseException)
00141   {
00142     return bind(index, static_cast<long>(value));
00143   }
00144 
00145   PreparedStatement & bind(const unsigned int index, const char * const value,
00146                            const unsigned int bytes,
00147                            const BindPolicy policy = BindTransient)
00148     SSRC_DECL_THROW(DatabaseException)
00149   {
00150     if(SQLite3::sqlite3_bind_text(_statement.get(), index, value,
00151                                   bytes,
00152                                   (policy == BindTransient ?
00153                                    SQLITE_TRANSIENT : SQLITE_STATIC)))
00154       throw DatabaseException(_statement);
00155     return *this;
00156   }
00157 
00158   PreparedStatement & bind(const unsigned int index, const char * const value,
00159                            const BindPolicy policy = BindTransient)
00160     SSRC_DECL_THROW(DatabaseException)
00161   {
00162     return bind(index, value, std::strlen(value), policy);
00163   }
00164 
00165   PreparedStatement & bind(const unsigned int index, const string & value,
00166                            const BindPolicy policy = BindTransient)
00167     SSRC_DECL_THROW(DatabaseException)
00168   {
00169     return bind(index, value.c_str(), value.size(), policy);
00170   }
00171 
00172   PreparedStatement & bind(const unsigned int index, const void *value,
00173                            const unsigned int bytes,
00174                            const BindPolicy policy = BindTransient)
00175     SSRC_DECL_THROW(DatabaseException)
00176   {
00177     if(SQLite3::sqlite3_bind_blob(_statement.get(), index, value, bytes,
00178                                   (policy == BindTransient ?
00179                                    SQLITE_TRANSIENT : SQLITE_STATIC)))
00180       throw DatabaseException(_statement);
00181     return *this;
00182   }
00183 
00184   PreparedStatement & bind(const unsigned int index, const blob_type & blob,
00185                            const BindPolicy policy = BindTransient)
00186     SSRC_DECL_THROW(DatabaseException)
00187   {
00188     return bind(index, blob.first, blob.second, policy);
00189   }
00190 
00191   /*
00192    * We allow client code to implement custom bind functions
00193    * by defining a function called bind_column with the following
00194    * signature:
00195    * PreparedStatement & bind_column(PreparedStatement & statement,
00196    *                                 const unsigned int index,
00197    *                                 const T & t);
00198    */
00199   template<typename T>
00200   PreparedStatement & bind(const unsigned int index, const T & t)
00201     SSRC_DECL_THROW(DatabaseException)
00202   {
00203     return bind_column(*this, index, t);
00204   }
00205 
00206   PreparedStatement & unbind(const unsigned int index)
00207     SSRC_DECL_THROW(DatabaseException)
00208   {
00209     if(SQLite3::sqlite3_bind_null(_statement.get(), index) != SQLITE_OK)
00210       throw DatabaseException(_statement);
00211     return *this;
00212   }
00213 
00214   PreparedStatement & unbind() SSRC_DECL_THROW(DatabaseException) {
00215     if(SQLite3::sqlite3_clear_bindings(_statement.get()) != SQLITE_OK)
00216       throw DatabaseException(_statement);
00217     return *this;
00218   }
00219 
00220   void reset() SSRC_DECL_THROW(DatabaseException) {
00221     if(SQLite3::sqlite3_reset(_statement.get()) != SQLITE_OK)
00222       throw DatabaseException(_statement);
00223   }
00224 
00225   QueryResult execute() SSRC_DECL_THROW(DatabaseException) {
00226     int result;
00227 
00228     result = detail::step(_statement);
00229 
00230     if(result == SQLITE_DONE) {
00231       return
00232         QueryResult(SQLite3::sqlite3_changes(SQLite3::sqlite3_db_handle(_statement.get())));
00233     }
00234 
00235     if(result != SQLITE_ROW)
00236       throw DatabaseException(_statement);
00237 
00238     return QueryResult(result_set_ptr(new ResultSet(_statement)));
00239   }
00240 
00241   template<typename functor>
00242   unsigned int for_each(const functor & apply) SSRC_DECL_THROW(DatabaseException) {
00243     QueryResult && result = execute();
00244 
00245     if(result.result_set)
00246       return result.result_set->for_each(apply);
00247 
00248     return 0;
00249   }
00250 
00251   // TODO: Move to ResultSetMetaData?  Add other metadata functions.
00252   unsigned int count_columns() {
00253     return SQLite3::sqlite3_column_count(_statement.get());
00254   }
00255 
00256   string column_name(const unsigned int index) {
00257     const char *name = SQLite3::sqlite3_column_name(_statement.get(), index);
00258 
00259     return (name ? name : string());
00260   }
00261 
00262 private:
00263 
00264   /* _bindp is a support function for compile-time expansion of bind
00265      sequence required by bindp.  It has the effect of producing
00266      bind(1,p1).bind(2,p2).bind(3,p3) ...
00267    */
00268 
00269   template<const unsigned int index, typename T>
00270   PreparedStatement & _bindp(const T & t)
00271     SSRC_DECL_THROW(DatabaseException)
00272   {
00273     return bind(index, t);
00274   }
00275 
00276   template<const unsigned int index, typename T, typename... P>
00277   PreparedStatement &
00278   _bindp(const T & t, const P & ...p)
00279     SSRC_DECL_THROW(DatabaseException)
00280   {
00281     return bind(index, t)._bindp<index + 1>(p...);
00282   }
00283 
00284 public:
00285 
00286   template<typename... P>
00287   PreparedStatement &  bindp(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
00288     return _bindp<1>(p...);
00289   }
00290 
00291   template<typename... P>
00292   QueryResult execute(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
00293     return bindp(p...).execute();
00294   }
00295 
00296   template<typename... P, typename functor>
00297   unsigned int for_each(const functor & apply, const P & ...p)
00298     SSRC_DECL_THROW(DatabaseException)
00299   {
00300     return bindp(p...).for_each(apply);
00301   }
00302 };
00303 
00304 typedef SSRC_UNIQUE_PTR<PreparedStatement> prepared_statement_ptr;
00305 
00306 class Database {
00307 
00308   sqlite3_ptr _database;
00309   prepared_statement_ptr _begin_transaction;
00310   prepared_statement_ptr _rollback_transaction;
00311   prepared_statement_ptr _end_transaction;
00312 
00313 public:
00314 
00315   explicit
00316   Database(const string & db_name = ":memory:") SSRC_DECL_THROW(DatabaseException) :
00317     _database(db_name)
00318   {
00319     if(_database.init_error())
00320       throw DatabaseException("database init error");
00321 
00322     _begin_transaction    = prepare("BEGIN");
00323     _rollback_transaction = prepare("ROLLBACK");
00324     _end_transaction      = prepare("END");
00325   }
00326 
00327   prepared_statement_ptr prepare(const string & statement)
00328     SSRC_DECL_THROW(DatabaseException)
00329   {
00330     return prepared_statement_ptr(new PreparedStatement(_database, statement));
00331   }
00332 
00333   void execute(const string & statement)
00334     SSRC_DECL_THROW(DatabaseException)
00335   {
00336     char *err = 0;
00337 
00338     if(SQLite3::sqlite3_exec(_database.get(), statement.c_str(), 0, 0, &err)
00339        != SQLITE_OK && err != 0)
00340     {
00341       string error(err);
00342       SQLite3::sqlite3_free(err);
00343       throw DatabaseException(error);
00344     }
00345   }
00346 
00347   bool auto_commit() {
00348     return SQLite3::sqlite3_get_autocommit(_database.get());
00349   }
00350 
00351   void begin_transaction() SSRC_DECL_THROW(DatabaseException) {
00352     _begin_transaction->execute();
00353   }
00354 
00355   void rollback_transaction() SSRC_DECL_THROW(DatabaseException) {
00356     _rollback_transaction->execute();
00357   }
00358 
00359   void end_transaction() SSRC_DECL_THROW(DatabaseException) {
00360     _end_transaction->execute();
00361   }
00362 };
00363 
00364 __END_NS_SSRC_WSPR_DATABASE
00365 
00366 #endif

Savarese Software Research Corporation
Copyright © 2006-2010 Savarese Software Research Corporation. All rights reserved.