LCOV - code coverage report
Current view: top level - pairinteraction - SQLite.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 87 97 89.7 %
Date: 2024-04-29 00:41:50 Functions: 29 30 96.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Sebastian Weber, Henri Menke. All rights reserved.
       3             :  *
       4             :  * This file is part of the pairinteraction library.
       5             :  *
       6             :  * The pairinteraction library is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU Lesser General Public License as published by
       8             :  * the Free Software Foundation, either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * The pairinteraction library is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with the pairinteraction library. If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #ifndef SQLITE_BINDINGS
      21             : #define SQLITE_BINDINGS
      22             : 
      23             : #include <iostream>
      24             : #include <memory>
      25             : #include <random>
      26             : #include <sstream>
      27             : #include <string>
      28             : #include <thread>
      29             : #include <type_traits>
      30             : 
      31             : #include <sqlite3.h>
      32             : #include <utility>
      33             : 
      34             : #include "utils.hpp"
      35             : 
      36             : namespace sqlite {
      37             : 
      38             : /** \brief SQLite error
      39             :  *
      40             :  * Exception to identify SQLite errors.
      41             :  * \code
      42             :  * try
      43             :  * {
      44             :  *   // code with sqlite
      45             :  * }
      46             :  * catch (sqlite::error &e)
      47             :  * {
      48             :  *   // handle exception
      49             :  * }
      50             :  * \endcode
      51             :  */
      52             : struct error : public std::exception {
      53             : public:
      54             :     /** \brief Constructor
      55             :      *
      56             :      * \param[in] err   error code
      57             :      * \param[in] msg   error message
      58             :      */
      59           5 :     explicit error(int err, std::string const &msg)
      60           5 :         : m_msg(std::string("SQLite error ") + std::to_string(err) + ": " + msg) {}
      61             : 
      62             :     /** \brief %what() function
      63             :      *
      64             :      * \returns error message
      65             :      */
      66           5 :     const char *what() const noexcept override { return m_msg.c_str(); }
      67             : 
      68             : private:
      69             :     std::string m_msg;
      70             : };
      71             : 
      72             : /** \brief SQLite Statement handle
      73             :  *
      74             :  * This object wraps an SQLite statement.  It is iterable in a while
      75             :  * loop with the step() function or in a range-based for loop.
      76             :  */
      77             : class statement {
      78             :     sqlite3 *m_db;
      79             :     std::unique_ptr<sqlite3_stmt, int (*)(sqlite3_stmt *)> m_stmt;
      80             :     std::string m_sql;
      81             :     bool m_prepared;
      82             :     bool m_valid;
      83             : 
      84       77313 :     void handle_error(int err) {
      85       77313 :         if (err != 0) {
      86           4 :             throw error(err, sqlite3_errstr(err));
      87             :         }
      88       77309 :     }
      89             : 
      90             : public:
      91             :     /** \brief Constructor
      92             :      *
      93             :      * This is called in the query function of the the handle object.
      94             :      *
      95             :      * \param[in] db    unmanaged raw pointer to the sqlite database
      96             :      */
      97        1540 :     explicit statement(sqlite3 *db) : statement{db, {}} {}
      98             : 
      99             :     /** \brief Constructor
     100             :      *
     101             :      * This is called in the query function of the the handle object.
     102             :      * This variant also sets a default query.
     103             :      *
     104             :      * \param[in] db    unmanaged raw pointer to the sqlite database
     105             :      * \param[in] sql   an initial query as a string
     106             :      */
     107        1631 :     explicit statement(sqlite3 *db, std::string sql)
     108        3262 :         : m_db{db}, m_stmt{nullptr, sqlite3_finalize}, m_sql{std::move(sql)},
     109        1631 :           m_prepared{false}, m_valid{true} {}
     110             : 
     111             :     /** \brief Set the query string
     112             :      *
     113             :      * \param[in] sql   the query string
     114             :      * \throws sqlite::error
     115             :      */
     116       12399 :     void set(std::string const &sql) {
     117       12399 :         m_sql = sql;
     118       12399 :         m_prepared = false;
     119       12399 :     }
     120             : 
     121             :     /** \overload void set(std::string const &sql) */
     122           1 :     void set(std::stringstream const &ss) { return set(ss.str()); }
     123             : 
     124             :     /** \brief Prepare the statement
     125             :      *
     126             :      * Prepare the statement for stepping.
     127             :      *
     128             :      * \throws sqlite::error
     129             :      */
     130        7940 :     void prepare() {
     131             :         sqlite3_stmt *pStmt; // is managed below
     132        7940 :         auto err = sqlite3_prepare_v2(m_db, m_sql.c_str(), -1, &pStmt, nullptr);
     133        7940 :         m_stmt.reset(pStmt);
     134             : 
     135        7940 :         handle_error(err);
     136             : 
     137        7939 :         m_prepared = true;
     138        7939 :     }
     139             : 
     140             :     /** \brief Step the statement
     141             :      *
     142             :      * This steps the statement.
     143             :      *
     144             :      * \returns true if there is a row, false otherwise
     145             :      *
     146             :      * \throws sqlite::error
     147             :      */
     148       13250 :     bool step() {
     149       13250 :         if (!m_prepared) {
     150           1 :             handle_error(SQLITE_MISUSE);
     151             :         }
     152       13249 :         if (!m_valid) {
     153           1 :             handle_error(SQLITE_DONE);
     154             :         }
     155             : 
     156       13248 :         auto err = sqlite3_step(m_stmt.get());
     157             : 
     158       13248 :         switch (err) {
     159        4615 :         case SQLITE_DONE:
     160        4615 :             m_valid = false;
     161        4615 :             return false;
     162        8633 :         case SQLITE_ROW:
     163        8633 :             m_valid = true;
     164        8633 :             return true;
     165             :         }
     166             : 
     167           0 :         m_valid = false;
     168           0 :         handle_error(err);
     169           0 :         return false;
     170             :     }
     171             : 
     172             :     /** \brief Reset the statement
     173             :      *
     174             :      * Reset the statement so you can rebind values.
     175             :      *
     176             :      * \throws sqlite::error
     177             :      */
     178       11253 :     void reset() {
     179       11253 :         handle_error(sqlite3_reset(m_stmt.get()));
     180       11253 :         m_valid = true;
     181       11253 :     }
     182             : 
     183             :     /** \brief Execute SQLite statements
     184             :      *
     185             :      * The SQL statements are passed to this function as a string and
     186             :      * are executed in-place.
     187             :      *
     188             :      * \param[in] sql   SQL statements
     189             :      * \throws sqlite::error
     190             :      */
     191        4551 :     void exec(std::string const &sql) {
     192        4551 :         set(sql);
     193        4551 :         auto err = sqlite3_exec(m_db, m_sql.c_str(), nullptr, nullptr, nullptr);
     194        4551 :         handle_error(err);
     195        4550 :     }
     196             : 
     197             :     /** \brief Bind a value to a position in the query
     198             :      *
     199             :      * \param[in] where   position in the query (one-based)
     200             :      * \param[in] s       string to bind
     201             :      * \throws sqlite::error
     202             :      */
     203        9865 :     void bind(int where, std::string const &s) {
     204        9865 :         handle_error(sqlite3_bind_text(m_stmt.get(), where, s.c_str(), s.length(), SQLITE_STATIC));
     205        9865 :     }
     206             : 
     207             :     /** \overload void bind(int where, std::string const &s) */
     208           2 :     void bind(int where, std::string &&s) {
     209           2 :         handle_error(
     210           2 :             sqlite3_bind_text(m_stmt.get(), where, s.c_str(), s.length(), SQLITE_TRANSIENT));
     211           2 :     }
     212             : 
     213             :     /** \overload void bind(int where, std::string const &s) */
     214       19037 :     void bind(int where, double d) { handle_error(sqlite3_bind_double(m_stmt.get(), where, d)); }
     215             : 
     216             :     /** \overload void bind(int where, std::string const &s) */
     217       24663 :     void bind(int where, int i) { handle_error(sqlite3_bind_int(m_stmt.get(), where, i)); }
     218             : 
     219             : #ifdef SCANNED_BY_DOXYGEN
     220             :     /** \brief Get value of a field
     221             :      *
     222             :      * Retrieve the value stored at position \p field as datatype \p
     223             :      * ReturnType.
     224             :      *
     225             :      * \tparam ReturnType   requested return type
     226             :      * \param[in]           field
     227             :      * \returns             the value stored in \p field as datatype \p
     228             :      * ReturnType
     229             :      */
     230             :     template <typename ReturnType>
     231             :     ReturnType get(int field);
     232             : #else
     233             :     template <typename T,
     234             :               typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
     235       21377 :     double get(int field) {
     236       21377 :         return sqlite3_column_double(m_stmt.get(), field);
     237             :     }
     238             : 
     239             :     template <typename T, typename = typename std::enable_if<std::is_same<T, int>::value>::type>
     240        5414 :     int get(int field) {
     241        5414 :         return sqlite3_column_int(m_stmt.get(), field);
     242             :     }
     243             : 
     244             :     template <typename T,
     245             :               typename = typename std::enable_if<std::is_same<T, std::string>::value>::type>
     246           4 :     std::string get(int field) {
     247           4 :         return std::string(
     248           8 :             reinterpret_cast<char const *>(sqlite3_column_text(m_stmt.get(), field)));
     249             :     }
     250             : #endif
     251             : 
     252             :     /** \brief Statement iterator
     253             :      *
     254             :      * The iterator builds upon Boost Iterator Facade to implement an
     255             :      * iterator over the statement with minimal boilerplate.  We refer
     256             :      * to the official documentation for the internal functions:
     257             :      * http://www.boost.org/libs/iterator/
     258             :      */
     259             :     class iterator {
     260             :     private:
     261             :         statement *m_stmt;
     262             :         bool m_done;
     263             :         bool m_end;
     264             : 
     265             :     public:
     266           2 :         iterator() : m_stmt{nullptr}, m_done{true}, m_end{true} {}
     267             : 
     268           1 :         explicit iterator(statement *p) : m_stmt{p}, m_done{false}, m_end{false} { m_stmt->step(); }
     269             : 
     270           3 :         statement &operator*() const {
     271             : #ifndef NDEBUG
     272           3 :             if (m_end)
     273           1 :                 throw std::out_of_range("iterator out of range");
     274             : #endif
     275           2 :             return *m_stmt;
     276             :         }
     277           2 :         iterator &operator++() {
     278           2 :             if (!m_done) {
     279           1 :                 m_done = m_stmt->step();
     280             :             } else {
     281           1 :                 m_end = true;
     282             :             }
     283           2 :             return *this;
     284             :         }
     285           3 :         bool operator!=(iterator const & /* unused */) const { return !m_end; }
     286             :     };
     287             : 
     288             :     /** \brief returns an iterator to the beginning
     289             :      *
     290             :      * With this, a statement can be iterated in a C++11 range-based for loop.
     291             :      *
     292             :      * \warning This is *not* zero overhead!  If you care about
     293             :      * performance use a while loop using step() instead.
     294             :      * \code
     295             :      * while(stmt.step())
     296             :      *     stmt.get<...>(...);
     297             :      * \endcode
     298             :      *
     299             :      * \returns iterator to the beginning
     300             :      */
     301           1 :     iterator begin() { return iterator{this}; }
     302             : 
     303             :     /** \brief returns an empty iterator
     304             :      *
     305             :      * The iterator for the SQLite statement is a forward iterator
     306             :      * which is implemented in terms of the step() function.  Since
     307             :      * the step() function will determine the end by itself, this
     308             :      * iterator is merely a sentinel.
     309             :      *
     310             :      * \returns empty iterator
     311             :      */
     312           2 :     iterator end() { return iterator{}; }
     313             : };
     314             : 
     315             : /** \brief SQLite Database handle
     316             :  *
     317             :  * This object handles the connection to the underlying SQLite databse.  It
     318             :  * provides a high-level object oriented RAII interface to the C-bindings of
     319             :  * SQLite3.
     320             :  */
     321             : class handle final {
     322             :     std::unique_ptr<sqlite3, decltype(&sqlite3_close)> m_db;
     323             :     int m_threshold;
     324             : 
     325           0 :     static int busy_handler(void *self, int num_prior_calls) {
     326           0 :         int thresh = static_cast<handle *>(self)->m_threshold;
     327             :         // Sleep if handler has been called less than num_prior_calls
     328           0 :         if (num_prior_calls < thresh) {
     329           0 :             std::this_thread::sleep_for(std::chrono::microseconds(utils::randint(2000, 20000)));
     330           0 :             return 1;
     331             :         }
     332             : 
     333           0 :         return 0; // Make sqlite3 return SQLITE_BUSY
     334             :     }
     335             : 
     336          99 :     void install_busy_handler() {
     337          99 :         auto err = sqlite3_busy_handler(m_db.get(), busy_handler, this);
     338             : 
     339          99 :         if (err != 0) {
     340           0 :             throw error(err, sqlite3_errmsg(m_db.get()));
     341             :         }
     342          99 :     }
     343             : 
     344             : public:
     345             :     /** \brief Conversion operator
     346             :      *
     347             :      * The handle object implicitly behaves like the underlying
     348             :      * pointer.
     349             :      *
     350             :      * \returns the raw database pointer
     351             :      */
     352      166305 :     operator sqlite3 *() { return m_db.get(); }
     353             : 
     354             :     /** \brief Constructor
     355             :      *
     356             :      * It takes a filename and a bitmask of parameters to open the
     357             :      * database connection.  This is useful if you have to open the
     358             :      * connection, e.g., read-only.  By default it uses the standard
     359             :      * flags for opening a database connection.  See
     360             :      * https://www.sqlite.org/c3ref/open.html for details.
     361             :      *
     362             :      * \param[in] filename    fully-qualified filename of the database
     363             :      * \param[in] flags       options to open the database (optional)
     364             :      * \throws sqlite::error
     365             :      */
     366         100 :     explicit handle(std::string const &filename,
     367             :                     int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
     368         101 :         : m_db{nullptr, sqlite3_close}, m_threshold{100000} {
     369             :         sqlite3 *tmp_db;
     370         100 :         auto err = sqlite3_open_v2(filename.c_str(), &tmp_db, flags, nullptr);
     371         100 :         m_db.reset(tmp_db);
     372             : 
     373         100 :         if (err != 0) {
     374           1 :             throw error(err, sqlite3_errmsg(m_db.get()));
     375             :         }
     376             : 
     377          99 :         install_busy_handler();
     378          99 :     }
     379             : 
     380             :     /** \brief Move constructor
     381             :      *
     382             :      * Reinstalls the busy handler.
     383             :      */
     384             :     handle(handle &&other) noexcept : m_db{std::move(other.m_db)}, m_threshold{other.m_threshold} {
     385             :         install_busy_handler();
     386             :     }
     387             : 
     388             :     /** \brief Move assignment operator
     389             :      *
     390             :      * Reinstalls the busy handler.
     391             :      */
     392             :     handle &operator=(handle &&other) noexcept {
     393             :         if (this != std::addressof(other)) // skip self-assignment
     394             :         {
     395             :             m_db = std::move(other.m_db);
     396             :             m_threshold = other.m_threshold;
     397             :             install_busy_handler();
     398             :         }
     399             :         return *this;
     400             :     }
     401             : };
     402             : 
     403             : } // namespace sqlite
     404             : 
     405             : #endif // SQLITE_BINDINGS

Generated by: LCOV version 1.14