Web Wispers 1.2.2 C++ Unit Test Coverage
Current view: top level - ssrc/wispers/database - Database.h (source / functions) Hit Total Coverage
Test: Web Wispers 1.2.2 C++ Unit Tests Lines: 87 98 88.8 %
Date: 2012-04-09 Functions: 95 95 100.0 %
Branches: 47 116 40.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright 2006-2009 Savarese Software Research Corporation
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at
       7                 :            :  *
       8                 :            :  *     https://www.savarese.com/software/ApacheLicense-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : /**
      18                 :            :  * @file
      19                 :            :  * This header defines the Database class.
      20                 :            :  */
      21                 :            : 
      22                 :            : #ifndef __SSRC_WSPR_DATABASE_DATABASE_H
      23                 :            : #define __SSRC_WSPR_DATABASE_DATABASE_H
      24                 :            : 
      25                 :            : #include <ssrc/wispers/database/types.h>
      26                 :            : #include <sstream>
      27                 :            : #include <cstring>
      28                 :            : 
      29                 :            : // TODO: Add support for handling SQLITE_BUSY.  Currently we throw!
      30                 :            : __BEGIN_NS_SSRC_WSPR_DATABASE
      31                 :            : 
      32                 :        266 : struct QueryResult {
      33                 :            :   unsigned int changes;
      34                 :            :   result_set_ptr result_set;
      35                 :            : 
      36                 :        157 :   explicit QueryResult(const unsigned int changes = 0) :
      37                 :        157 :     changes(changes), result_set()
      38                 :        157 :   { }
      39                 :            : 
      40                 :        103 :   explicit QueryResult(const result_set_ptr & result_set) :
      41                 :        103 :     changes(0), result_set(result_set)
      42                 :        103 :   { }
      43                 :            : };
      44                 :            : 
      45                 :            : // Forward declaration;
      46                 :            : 
      47                 :        868 : class PreparedStatement {
      48                 :            :   friend class Database;
      49                 :            : 
      50                 :            :   sqlite3_stmt_ptr _statement;
      51                 :            : 
      52                 :        868 :   PreparedStatement(sqlite3_ptr & db, const string & statement) :
      53                 :            :     _statement(detail::sqlite3_stmt_initializer(db, statement),
      54                 :        868 :                detail::sqlite3_stmt_init())
      55                 :            :   {
      56         [ -  + ]:        868 :     if(_statement.init_error())
      57         [ #  # ]:          0 :       throw DatabaseException(_statement);
      58                 :        868 :   }
      59                 :            : 
      60                 :            : public:
      61                 :            : 
      62                 :            :   enum BindPolicy { BindTransient, BindStatic };
      63                 :            : 
      64                 :          5 :   bool expired() {
      65                 :          5 :     return SQLite3::sqlite3_expired(_statement.get());
      66                 :            :   }
      67                 :            : 
      68                 :          5 :   unsigned int count_parameters() {
      69                 :          5 :     return SQLite3::sqlite3_bind_parameter_count(_statement.get());
      70                 :            :   }
      71                 :            : 
      72                 :            :   PreparedStatement & bind(const unsigned int index, const double value)
      73                 :            :     SSRC_DECL_THROW(DatabaseException)
      74                 :            :   {
      75                 :            :     if(SQLite3::sqlite3_bind_double(_statement.get(), index, value)
      76                 :            :        != SQLITE_OK)
      77                 :            :       throw DatabaseException(_statement);
      78                 :            :     return *this;
      79                 :            :   }
      80                 :            : 
      81                 :        263 :   PreparedStatement & bind(const unsigned int index, const int value)
      82                 :            :     SSRC_DECL_THROW(DatabaseException)
      83                 :            :   {
      84         [ -  + ]:        263 :     if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
      85         [ #  # ]:          0 :       throw DatabaseException(_statement);
      86                 :        263 :     return *this;
      87                 :            :   }
      88                 :            : 
      89                 :        101 :   PreparedStatement & bind(const unsigned int index, const unsigned int value)
      90                 :            :     SSRC_DECL_THROW(DatabaseException)
      91                 :            :   {
      92                 :        101 :     return bind(index, static_cast<int>(value));
      93                 :            :   }
      94                 :            : 
      95                 :            :   PreparedStatement & bind(const unsigned int index, const std::int16_t value)
      96                 :            :     SSRC_DECL_THROW(DatabaseException)
      97                 :            :   {
      98                 :            :     return bind(index, static_cast<int>(value));
      99                 :            :   }
     100                 :            : 
     101                 :         66 :   PreparedStatement & bind(const unsigned int index, const std::uint16_t value)
     102                 :            :     SSRC_DECL_THROW(DatabaseException)
     103                 :            :   {
     104                 :         66 :     return bind(index, static_cast<int>(value));
     105                 :            :   }
     106                 :            : 
     107                 :            :   PreparedStatement & bind(const unsigned int index, const bool value)
     108                 :            :     SSRC_DECL_THROW(DatabaseException)
     109                 :            :   {
     110                 :            :     if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
     111                 :            :       throw DatabaseException(_statement);
     112                 :            :     return *this;
     113                 :            :   }
     114                 :            : 
     115                 :        277 :   PreparedStatement & bind(const unsigned int index, const std::int64_t value)
     116                 :            :     SSRC_DECL_THROW(DatabaseException)
     117                 :            :   {
     118         [ -  + ]:        277 :     if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
     119                 :            :        != SQLITE_OK)
     120         [ #  # ]:          0 :       throw DatabaseException(_statement);
     121                 :        277 :     return *this;
     122                 :            :   }
     123                 :            : 
     124                 :        118 :   PreparedStatement & bind(const unsigned int index, const std::uint64_t value)
     125                 :            :     SSRC_DECL_THROW(DatabaseException)
     126                 :            :   {
     127                 :        118 :     return bind(index, static_cast<std::int64_t>(value));
     128                 :            :   }
     129                 :            : 
     130                 :            : #if (LONG_MAX == INT_MAX)
     131                 :            :   PreparedStatement & bind(const unsigned int index, const long value)
     132                 :            :     SSRC_DECL_THROW(DatabaseException)
     133                 :            :   {
     134                 :            :     if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
     135                 :            :        != SQLITE_OK)
     136                 :            :       throw DatabaseException(_statement);
     137                 :            :     return *this;
     138                 :            :   }
     139                 :            : 
     140                 :            :   PreparedStatement & bind(const unsigned int index, const unsigned long value)
     141                 :            :     SSRC_DECL_THROW(DatabaseException)
     142                 :            :   {
     143                 :            :     return bind(index, static_cast<long>(value));
     144                 :            :   }
     145                 :            : #endif
     146                 :            : 
     147                 :        110 :   PreparedStatement & bind(const unsigned int index, const char * const value,
     148                 :            :                            const unsigned int bytes,
     149                 :            :                            const BindPolicy policy = BindTransient)
     150                 :            :     SSRC_DECL_THROW(DatabaseException)
     151                 :            :   {
     152         [ -  + ]:        110 :     if(SQLite3::sqlite3_bind_text(_statement.get(), index, value,
     153                 :            :                                   bytes,
     154                 :            :                                   (policy == BindTransient ?
     155         [ +  - ]:        110 :                                    SQLITE_TRANSIENT : SQLITE_STATIC)))
     156         [ #  # ]:          0 :       throw DatabaseException(_statement);
     157                 :        110 :     return *this;
     158                 :            :   }
     159                 :            : 
     160                 :          1 :   PreparedStatement & bind(const unsigned int index, const char * const value,
     161                 :            :                            const BindPolicy policy = BindTransient)
     162                 :            :     SSRC_DECL_THROW(DatabaseException)
     163                 :            :   {
     164                 :          1 :     return bind(index, value, std::strlen(value), policy);
     165                 :            :   }
     166                 :            : 
     167                 :        109 :   PreparedStatement & bind(const unsigned int index, const string & value,
     168                 :            :                            const BindPolicy policy = BindTransient)
     169                 :            :     SSRC_DECL_THROW(DatabaseException)
     170                 :            :   {
     171                 :        109 :     return bind(index, value.c_str(), value.size(), policy);
     172                 :            :   }
     173                 :            : 
     174                 :          2 :   PreparedStatement & bind(const unsigned int index, const void *value,
     175                 :            :                            const unsigned int bytes,
     176                 :            :                            const BindPolicy policy = BindTransient)
     177                 :            :     SSRC_DECL_THROW(DatabaseException)
     178                 :            :   {
     179         [ -  + ]:          2 :     if(SQLite3::sqlite3_bind_blob(_statement.get(), index, value, bytes,
     180                 :            :                                   (policy == BindTransient ?
     181         [ +  - ]:          2 :                                    SQLITE_TRANSIENT : SQLITE_STATIC)))
     182         [ #  # ]:          0 :       throw DatabaseException(_statement);
     183                 :          2 :     return *this;
     184                 :            :   }
     185                 :            : 
     186                 :          1 :   PreparedStatement & bind(const unsigned int index, const blob_type & blob,
     187                 :            :                            const BindPolicy policy = BindTransient)
     188                 :            :     SSRC_DECL_THROW(DatabaseException)
     189                 :            :   {
     190                 :          1 :     return bind(index, blob.first, blob.second, policy);
     191                 :            :   }
     192                 :            : 
     193                 :            :   /*
     194                 :            :    * We allow client code to implement custom bind functions
     195                 :            :    * by defining a function called bind_column with the following
     196                 :            :    * signature:
     197                 :            :    * PreparedStatement & bind_column(PreparedStatement & statement,
     198                 :            :    *                                 const unsigned int index,
     199                 :            :    *                                 const T & t);
     200                 :            :    */
     201                 :            :   template<typename T>
     202                 :          1 :   PreparedStatement & bind(const unsigned int index, const T & t)
     203                 :            :     SSRC_DECL_THROW(DatabaseException)
     204                 :            :   {
     205                 :          1 :     return bind_column(*this, index, t);
     206                 :            :   }
     207                 :            : 
     208                 :            :   PreparedStatement & unbind(const unsigned int index)
     209                 :            :     SSRC_DECL_THROW(DatabaseException)
     210                 :            :   {
     211                 :            :     if(SQLite3::sqlite3_bind_null(_statement.get(), index) != SQLITE_OK)
     212                 :            :       throw DatabaseException(_statement);
     213                 :            :     return *this;
     214                 :            :   }
     215                 :            : 
     216                 :            :   PreparedStatement & unbind() SSRC_DECL_THROW(DatabaseException) {
     217                 :            :     if(SQLite3::sqlite3_clear_bindings(_statement.get()) != SQLITE_OK)
     218                 :            :       throw DatabaseException(_statement);
     219                 :            :     return *this;
     220                 :            :   }
     221                 :            : 
     222                 :         70 :   void reset() SSRC_DECL_THROW(DatabaseException) {
     223         [ -  + ]:         70 :     if(SQLite3::sqlite3_reset(_statement.get()) != SQLITE_OK)
     224         [ #  # ]:          0 :       throw DatabaseException(_statement);
     225                 :         70 :   }
     226                 :            : 
     227                 :        260 :   QueryResult execute() SSRC_DECL_THROW(DatabaseException) {
     228                 :            :     int result;
     229                 :            : 
     230                 :        260 :     result = detail::step(_statement);
     231                 :            : 
     232         [ +  + ]:        260 :     if(result == SQLITE_DONE) {
     233                 :            :       return
     234                 :        157 :         QueryResult(SQLite3::sqlite3_changes(SQLite3::sqlite3_db_handle(_statement.get())));
     235                 :            :     }
     236                 :            : 
     237         [ -  + ]:        103 :     if(result != SQLITE_ROW)
     238         [ #  # ]:          0 :       throw DatabaseException(_statement);
     239                 :            : 
     240   [ +  -  +  - ]:        103 :     return QueryResult(result_set_ptr(new ResultSet(_statement)));
     241                 :            :   }
     242                 :            : 
     243                 :            :   template<typename functor>
     244                 :         35 :   unsigned int for_each(const functor & apply) SSRC_DECL_THROW(DatabaseException) {
     245                 :         70 :     QueryResult && result = execute();
     246                 :            : 
     247   [ +  +  +  +  :         35 :     if(result.result_set)
                   +  + ]
     248   [ +  -  +  -  :         30 :       return result.result_set->for_each(apply);
                   +  - ]
     249                 :            : 
     250                 :          5 :     return 0;
     251                 :            :   }
     252                 :            : 
     253                 :            :   // TODO: Move to ResultSetMetaData?  Add other metadata functions.
     254                 :         11 :   unsigned int count_columns() {
     255                 :         11 :     return SQLite3::sqlite3_column_count(_statement.get());
     256                 :            :   }
     257                 :            : 
     258                 :          2 :   string column_name(const unsigned int index) {
     259                 :          2 :     const char *name = SQLite3::sqlite3_column_name(_statement.get(), index);
     260                 :            : 
     261   [ +  -  +  -  :          2 :     return (name ? name : string());
          #  #  +  -  #  
                      # ]
     262                 :            :   }
     263                 :            : 
     264                 :            : private:
     265                 :            : 
     266                 :            :   /* _bindp is a support function for compile-time expansion of bind
     267                 :            :      sequence required by bindp.  It has the effect of producing
     268                 :            :      bind(1,p1).bind(2,p2).bind(3,p3) ...
     269                 :            :    */
     270                 :            : 
     271                 :            :   template<const unsigned int index, typename T>
     272                 :        226 :   PreparedStatement & _bindp(const T & t)
     273                 :            :     SSRC_DECL_THROW(DatabaseException)
     274                 :            :   {
     275                 :        226 :     return bind(index, t);
     276                 :            :   }
     277                 :            : 
     278                 :            :   template<const unsigned int index, typename T, typename... P>
     279                 :            :   PreparedStatement &
     280                 :        403 :   _bindp(const T & t, const P & ...p)
     281                 :            :     SSRC_DECL_THROW(DatabaseException)
     282                 :            :   {
     283                 :        403 :     return bind(index, t)._bindp<index + 1>(p...);
     284                 :            :   }
     285                 :            : 
     286                 :            : public:
     287                 :            : 
     288                 :            :   template<typename... P>
     289                 :        226 :   PreparedStatement &  bindp(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
     290                 :        226 :     return _bindp<1>(p...);
     291                 :            :   }
     292                 :            : 
     293                 :            :   template<typename... P>
     294                 :         32 :   QueryResult execute(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
     295                 :         32 :     return bindp(p...).execute();
     296                 :            :   }
     297                 :            : 
     298                 :            :   template<typename... P, typename functor>
     299                 :         31 :   unsigned int for_each(const functor & apply, const P & ...p)
     300                 :            :     SSRC_DECL_THROW(DatabaseException)
     301                 :            :   {
     302                 :         31 :     return bindp(p...).for_each(apply);
     303                 :            :   }
     304                 :            : };
     305                 :            : 
     306                 :            : typedef SSRC_UNIQUE_PTR<PreparedStatement> prepared_statement_ptr;
     307                 :            : 
     308   [ +  -  +  -  :         23 : class Database {
                   +  - ]
     309                 :            : 
     310                 :            :   sqlite3_ptr _database;
     311                 :            :   prepared_statement_ptr _begin_transaction;
     312                 :            :   prepared_statement_ptr _rollback_transaction;
     313                 :            :   prepared_statement_ptr _end_transaction;
     314                 :            : 
     315                 :            : public:
     316                 :            : 
     317                 :            :   explicit
     318                 :         23 :   Database(const string & db_name = ":memory:") SSRC_DECL_THROW(DatabaseException) :
     319                 :         23 :     _database(db_name)
     320                 :            :   {
     321         [ -  + ]:         23 :     if(_database.init_error())
     322   [ #  #  #  # ]:          0 :       throw DatabaseException("database init error");
     323                 :            : 
     324   [ +  -  +  -  :         23 :     _begin_transaction    = prepare("BEGIN");
          +  -  +  -  +  
                      - ]
     325   [ +  -  +  -  :         23 :     _rollback_transaction = prepare("ROLLBACK");
          +  -  +  -  +  
                      - ]
     326   [ +  -  +  -  :         23 :     _end_transaction      = prepare("END");
          +  -  +  -  +  
                      - ]
     327                 :         23 :   }
     328                 :            : 
     329                 :        868 :   prepared_statement_ptr prepare(const string & statement)
     330                 :            :     SSRC_DECL_THROW(DatabaseException)
     331                 :            :   {
     332         [ +  - ]:        868 :     return prepared_statement_ptr(new PreparedStatement(_database, statement));
     333                 :            :   }
     334                 :            : 
     335                 :          4 :   void execute(const string & statement)
     336                 :            :     SSRC_DECL_THROW(DatabaseException)
     337                 :            :   {
     338                 :          4 :     char *err = 0;
     339                 :            : 
     340   [ -  +  #  #  :          4 :     if(SQLite3::sqlite3_exec(_database.get(), statement.c_str(), 0, 0, &err)
                   -  + ]
     341                 :            :        != SQLITE_OK && err != 0)
     342                 :            :     {
     343         [ #  # ]:          0 :       string error(err);
     344         [ #  # ]:          0 :       SQLite3::sqlite3_free(err);
     345         [ #  # ]:          0 :       throw DatabaseException(error);
     346                 :            :     }
     347                 :          4 :   }
     348                 :            : 
     349                 :            :   bool auto_commit() {
     350                 :            :     return SQLite3::sqlite3_get_autocommit(_database.get());
     351                 :            :   }
     352                 :            : 
     353                 :            :   void begin_transaction() SSRC_DECL_THROW(DatabaseException) {
     354                 :            :     _begin_transaction->execute();
     355                 :            :   }
     356                 :            : 
     357                 :            :   void rollback_transaction() SSRC_DECL_THROW(DatabaseException) {
     358                 :            :     _rollback_transaction->execute();
     359                 :            :   }
     360                 :            : 
     361                 :            :   void end_transaction() SSRC_DECL_THROW(DatabaseException) {
     362                 :            :     _end_transaction->execute();
     363                 :            :   }
     364                 :            : };
     365                 :            : 
     366                 :            : __END_NS_SSRC_WSPR_DATABASE
     367                 :            : 
     368                 :            : #endif