LCOV - code coverage report
Current view: top level - src/sl3 - command.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 98.7 % 150 148
Test Date: 2024-12-09 18:45:33 Functions: 95.8 % 24 23
Branches: 65.3 % 173 113

             Branch data     Line data    Source code
       1                 :             : /******************************************************************************
       2                 :             :  ------------- Copyright (c) 2009-2023 H a r a l d  A c h i t z ---------------
       3                 :             :  ---------- < h a r a l d dot a c h i t z at g m a i l dot c o m > ------------
       4                 :             :  ---- This Source Code Form is subject to the terms of the Mozilla Public -----
       5                 :             :  ---- License, v. 2.0. If a copy of the MPL was not distributed with this -----
       6                 :             :  ---------- file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------
       7                 :             :  ******************************************************************************/
       8                 :             : 
       9                 :             : #include <sl3/command.hpp>
      10                 :             : 
      11                 :             : #include <functional>
      12                 :             : 
      13                 :             : #include <sqlite3.h>
      14                 :             : 
      15                 :             : #include "../sl3/connection.hpp"
      16                 :             : #include <sl3/columns.hpp>
      17                 :             : #include <sl3/database.hpp>
      18                 :             : #include <sl3/error.hpp>
      19                 :             : 
      20                 :             : #include "utils.hpp"
      21                 :             : 
      22                 :             : namespace sl3
      23                 :             : {
      24                 :             :   namespace
      25                 :             :   {
      26                 :             :     sqlite3_stmt*
      27                 :         118 :     createStmt (sqlite3* db, const std::string& sql)
      28                 :             :     {
      29         [ +  + ]:         118 :       if (db == nullptr)
      30         [ +  - ]:           8 :         throw ErrNoConnection{};
      31                 :             : 
      32                 :         110 :       sqlite3_stmt* stmt       = nullptr;
      33                 :         110 :       const char*   unussedSQL = nullptr;
      34                 :             : 
      35         [ +  - ]:         110 :       int rc = sqlite3_prepare_v2 (db, sql.c_str (), -1, &stmt, &unussedSQL);
      36                 :             : 
      37         [ +  + ]:         110 :       if (rc != SQLITE_OK)
      38                 :             :         {
      39   [ +  -  +  - ]:           8 :           SQLite3Error sl3error (rc, sqlite3_errmsg (db));
      40                 :           8 :           throw sl3error;
      41                 :           8 :         }
      42                 :             : 
      43                 :         102 :       return stmt;
      44                 :             :     }
      45                 :             : 
      46                 :             :     DbValues
      47                 :          94 :     createParameters (sqlite3_stmt* stmt)
      48                 :             :     {
      49                 :          94 :       const size_t paracount = as_size_t (sqlite3_bind_parameter_count (stmt));
      50                 :             : 
      51                 :             :       using container = DbValues::container_type;
      52                 :             : 
      53   [ +  -  +  +  :         124 :       return paracount > 0 ? DbValues (container (paracount, Type::Variant))
          +  +  +  +  -  
             -  -  -  -  
                      - ]
      54         [ +  + ]:         203 :                            : DbValues ();
      55                 :             :     }
      56                 :             : 
      57                 :             :     void
      58                 :          97 :     bind (sqlite3_stmt* stmt, DbValues& parameters)
      59                 :             :     {
      60                 :          97 :       int curParaNr = 0;
      61   [ +  -  +  -  :         156 :       for (auto& val : parameters)
                   +  + ]
      62                 :             :         {
      63                 :          59 :           curParaNr += 1; // sqlite starts at 1
      64                 :             : 
      65                 :             :           int rc;
      66                 :             : 
      67   [ +  -  +  +  :          59 :           switch (val.type ())
             +  +  +  - ]
      68                 :             :             {
      69                 :          27 :             case Type::Int:
      70   [ +  -  +  - ]:          27 :               rc = sqlite3_bind_int64 (stmt, curParaNr, val.getInt ());
      71                 :          27 :               break;
      72                 :             : 
      73                 :          11 :             case Type::Real:
      74   [ +  -  +  - ]:          11 :               rc = sqlite3_bind_double (stmt, curParaNr, val.getReal ());
      75                 :          11 :               break;
      76                 :             : 
      77                 :          13 :             case Type::Text:
      78                 :             :               // note, i do not want \0 in the db so take size
      79                 :             :               // SQLITE_TRANSIENT would copy the string , is unwanted here
      80         [ +  - ]:          13 :               rc = sqlite3_bind_text (
      81                 :             :                   stmt,
      82                 :             :                   curParaNr,
      83         [ +  - ]:          13 :                   val.getText ().c_str (),
      84         [ +  - ]:          13 :                   static_cast<int> (val.getText ().size ()),
      85                 :             :                   SQLITE_STATIC);
      86                 :             : 
      87                 :          13 :               break;
      88                 :             : 
      89                 :           4 :             case Type::Blob:
      90         [ +  - ]:           4 :               rc = sqlite3_bind_blob (
      91                 :             :                   stmt,
      92                 :             :                   curParaNr,
      93         [ +  - ]:           4 :                   &(val.getBlob ()[0]),
      94         [ +  - ]:           4 :                   static_cast<int> (val.getBlob ().size ()),
      95                 :             :                   SQLITE_STATIC);
      96                 :             : 
      97                 :           4 :               break;
      98                 :             : 
      99                 :           4 :             case Type::Null:
     100         [ +  - ]:           4 :               rc = sqlite3_bind_null (stmt, curParaNr);
     101                 :           4 :               break;
     102                 :             : 
     103                 :           0 :             default:
     104                 :             :               throw ErrUnexpected (); // LCOV_EXCL_LINE
     105                 :             :             }
     106                 :             : 
     107         [ -  + ]:          59 :           if (rc != SQLITE_OK)
     108                 :             :             throw sl3::SQLite3Error (rc, ""); // LCOV_EXCL_LINE TODO ho to test
     109                 :             :         }
     110                 :          97 :     }
     111                 :             : 
     112                 :             :   } // ns
     113                 :             : 
     114                 :         109 :   Command::Command (Connection connection, const std::string& sql)
     115                 :         109 :   : _connection (std::move (connection))
     116         [ +  + ]:         109 :   , _stmt (createStmt (_connection->db (), sql))
     117         [ +  - ]:          94 :   , _parameters (createParameters (_stmt))
     118                 :             :   {
     119                 :         109 :   }
     120                 :             : 
     121                 :           9 :   Command::Command (Connection         connection,
     122                 :             :                     const std::string& sql,
     123                 :           9 :                     DbValues           parameters)
     124                 :           9 :   : _connection (std::move (connection))
     125         [ +  + ]:           9 :   , _stmt (createStmt (_connection->db (), sql))
     126                 :           8 :   , _parameters (std::move (parameters))
     127                 :             :   {
     128         [ +  - ]:           8 :     const size_t paracount = as_size_t (sqlite3_bind_parameter_count (_stmt));
     129                 :             : 
     130         [ +  + ]:           8 :     if (paracount != _parameters.size ())
     131                 :             :       {
     132         [ +  - ]:           4 :         throw ErrTypeMisMatch ("Incorrect parameter count");
     133                 :             :       }
     134                 :          13 :   }
     135                 :             : 
     136                 :           1 :   Command::Command (Command&& other)
     137                 :           1 :   : _connection (std::move (other._connection))
     138                 :           1 :   , _stmt (other._stmt)
     139                 :           1 :   , _parameters (std::move (other._parameters))
     140                 :             :   { // clear stm so that d'tor ot other does no action
     141                 :           1 :     other._stmt = nullptr;
     142                 :           1 :   }
     143                 :             : 
     144                 :          99 :   Command::~Command ()
     145                 :             :   {
     146         [ +  + ]:          99 :     if (_stmt) // its not a moved from zombi
     147                 :             :       {
     148         [ +  - ]:          98 :         if (_connection->isValid ()) // otherwise database will have done this
     149                 :             :           {
     150                 :          98 :             sqlite3_finalize (_stmt);
     151                 :             :           }
     152                 :             :       }
     153                 :          99 :   }
     154                 :             : 
     155                 :             :   Dataset
     156                 :          18 :   Command::select ()
     157                 :             :   {
     158         [ +  - ]:          18 :     return select (DbValues (), Types ());
     159                 :             :   }
     160                 :             : 
     161                 :             :   Dataset
     162                 :          20 :   Command::select (const Types& types, const DbValues& parameters)
     163                 :             :   {
     164                 :          20 :     return select (parameters, types);
     165                 :             :   }
     166                 :             : 
     167                 :             :   Dataset
     168                 :          38 :   Command::select (const DbValues& parameters, const Types& types)
     169                 :             :   {
     170   [ +  -  +  - ]:          38 :     Dataset  ds{types};
     171                 :         129 :     Callback fillds = [&ds] (Columns columns) -> bool {
     172         [ +  + ]:          53 :       if (ds._names.size () == 0)
     173                 :             :         {
     174                 :          38 :           const int typeCount = static_cast<int> (ds._fieldtypes.size ());
     175                 :             : 
     176         [ +  + ]:          38 :           if (typeCount == 0)
     177                 :             :             {
     178                 :             :               using container_type = Types::container_type;
     179   [ +  -  +  - ]:          18 :               container_type c (as_size_t (columns.count ()), Type::Variant);
     180         [ +  - ]:          18 :               Types          fieldtypes{c};
     181                 :          18 :               ds._fieldtypes.swap (fieldtypes);
     182                 :          18 :             }
     183         [ +  + ]:          20 :           else if (typeCount != columns.count ())
     184                 :             :             {
     185                 :             :               throw ErrTypeMisMatch (
     186         [ +  - ]:           1 :                   "DbValuesTypeList.size != queryrow.getColumnCount()");
     187                 :             :             }
     188         [ +  - ]:          37 :           ds._names = columns.getNames ();
     189                 :             :         }
     190                 :             : 
     191                 :             :       // this will throw if a type does not match.
     192   [ +  +  +  - ]:          52 :       ds._cont.emplace_back (columns.getRow (ds._fieldtypes));
     193                 :             : 
     194                 :          50 :       return true;
     195                 :          38 :     };
     196                 :             : 
     197   [ +  -  +  + ]:          41 :     execute (fillds, parameters);
     198                 :          70 :     return ds;
     199                 :          41 :   }
     200                 :             : 
     201                 :             :   void
     202                 :           5 :   Command::execute ()
     203                 :             :   {
     204         [ +  + ]:           5 :     execute (DbValues ());
     205                 :           4 :   }
     206                 :             : 
     207                 :             :   void
     208                 :          19 :   Command::execute (const DbValues& parameters)
     209                 :             :   {
     210         [ +  + ]:          21 :     execute ([] (Columns) -> bool { return true; }, parameters);
     211                 :          17 :   }
     212                 :             : 
     213                 :             :   void
     214                 :           6 :   Command::execute (RowCallback& cb, const DbValues& parameters)
     215                 :             :   {
     216         [ +  - ]:           6 :     cb.onStart ();
     217                 :             : 
     218                 :          11 :     auto cbf = [&cb] (Columns cols) -> bool { return cb.onRow (cols); };
     219                 :             : 
     220         [ +  - ]:           6 :     execute (cbf, parameters);
     221                 :             : 
     222         [ +  - ]:           6 :     cb.onEnd ();
     223                 :           6 :   }
     224                 :             : 
     225                 :             :   void
     226                 :          98 :   Command::execute (Callback callback, const DbValues& parameters)
     227                 :             :   {
     228         [ +  - ]:          98 :     _connection->ensureValid ();
     229                 :             : 
     230         [ +  + ]:          98 :     if (parameters.size () > 0)
     231         [ +  + ]:          15 :       setParameters (parameters);
     232                 :             : 
     233         [ +  - ]:          97 :     bind (_stmt, _parameters);
     234                 :             : 
     235                 :             :     // use this to ensure a reset of _stmt
     236                 :             :     using ResetGuard
     237                 :             :         = std::unique_ptr<sqlite3_stmt, decltype (&sqlite3_reset)>;
     238                 :          97 :     ResetGuard resetGuard (_stmt, &sqlite3_reset);
     239                 :             : 
     240                 :          97 :     bool loop = true;
     241         [ +  + ]:         265 :     while (loop)
     242                 :             :       {
     243         [ +  - ]:         173 :         int rc = sqlite3_step (_stmt);
     244                 :             : 
     245      [ +  +  + ]:         173 :         switch (rc)
     246                 :             :           {
     247                 :          68 :           case SQLITE_OK:
     248                 :             :           case SQLITE_DONE:
     249                 :             :             {
     250                 :          68 :               loop = false;
     251                 :          68 :               break;
     252                 :             :             }
     253                 :         104 :           case SQLITE_ROW:
     254                 :             :             {
     255   [ +  -  +  + ]:         104 :               loop = callback (Columns{_stmt});
     256                 :         100 :               break;
     257                 :             :             }
     258                 :             : 
     259                 :           1 :           default:
     260                 :             :             {
     261         [ +  - ]:           1 :               auto         db = sqlite3_db_handle (_stmt);
     262   [ +  -  +  - ]:           1 :               SQLite3Error sl3error (rc, sqlite3_errmsg (db));
     263                 :           1 :               throw sl3error;
     264                 :           1 :             }
     265                 :             :           }
     266                 :             :       }
     267                 :          97 :   }
     268                 :             : 
     269                 :             :   DbValues&
     270                 :           2 :   Command::getParameters ()
     271                 :             :   {
     272                 :           2 :     return _parameters;
     273                 :             :   }
     274                 :             : 
     275                 :             :   const DbValues&
     276                 :           1 :   Command::getParameters () const
     277                 :             :   {
     278                 :           1 :     return _parameters;
     279                 :             :   }
     280                 :             : 
     281                 :             :   DbValue&
     282                 :           2 :   Command::getParameter (int idx)
     283                 :             :   {
     284                 :           2 :     return _parameters.at (as_size_t (idx));
     285                 :             :   }
     286                 :             : 
     287                 :             :   const DbValue&
     288                 :           2 :   Command::getParameter (int idx) const
     289                 :             :   {
     290                 :           2 :     return _parameters.at (as_size_t (idx));
     291                 :             :   }
     292                 :             : 
     293                 :             :   void
     294                 :          17 :   Command::setParameters (const DbValues& values)
     295                 :             :   {
     296         [ +  + ]:          17 :     if (values.size () != _parameters.size ())
     297                 :             :       {
     298         [ +  - ]:           2 :         throw ErrTypeMisMatch ("parameter size incorrect");
     299                 :             :       }
     300                 :             : 
     301         [ +  + ]:          61 :     for (size_t i = 0; i < values.size (); ++i)
     302                 :             :       {
     303                 :          47 :         _parameters[i] = values[i];
     304                 :             :       }
     305                 :          14 :   }
     306                 :             : 
     307                 :             :   void
     308                 :           3 :   Command::resetParameters (DbValues values)
     309                 :             :   {
     310   [ +  +  +  -  :           7 :     ASSERT_EXCEPT (values.size () == _parameters.size (), ErrTypeMisMatch);
          +  -  +  -  +  
                      - ]
     311                 :             :     // auto tmp = values;
     312                 :           1 :     _parameters.swap (values);
     313                 :           1 :   }
     314                 :             : 
     315                 :             :   std::vector<std::string>
     316                 :           3 :   Command::getParameterNames () const
     317                 :             :   {
     318                 :           3 :     std::vector<std::string> names;
     319         [ +  - ]:           3 :     names.resize (_parameters.size ());
     320                 :             : 
     321         [ +  + ]:          11 :     for (unsigned int i = 0; i < _parameters.size (); ++i)
     322                 :             :       {
     323                 :             :         const char* chrname
     324         [ +  - ]:           8 :             = sqlite3_bind_parameter_name (_stmt, as_int (i + 1));
     325   [ +  -  +  - ]:           8 :         names[i] = chrname ? chrname : "";
     326                 :             :       }
     327                 :           3 :     return names;
     328                 :           0 :   }
     329                 :             : 
     330                 :             : } // ns
        

Generated by: LCOV version 2.0-1