aboutsummaryrefslogtreecommitdiff
path: root/external/glim/sqlite.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'external/glim/sqlite.hpp')
-rw-r--r--external/glim/sqlite.hpp538
1 files changed, 0 insertions, 538 deletions
diff --git a/external/glim/sqlite.hpp b/external/glim/sqlite.hpp
deleted file mode 100644
index c7e92ac4a..000000000
--- a/external/glim/sqlite.hpp
+++ /dev/null
@@ -1,538 +0,0 @@
-#ifndef GLIM_SQLITE_HPP_
-#define GLIM_SQLITE_HPP_
-
-/**
- * A threaded interface to <a href="http://sqlite.org/">SQLite</a>.
- * This file is a header-only library,
- * whose sole dependencies should be standard STL and posix threading libraries.
- * You can extract this file out of the "glim" library to include it separately in your project.
- * @code
-Copyright 2006-2012 Kozarezov Artem Aleksandrovich
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- * @endcode
- * @file
- */
-
-#include <stdexcept>
-#include <string>
-#include <sqlite3.h>
-#include <pthread.h>
-#include <string.h> // strerror
-#include <sys/types.h> // stat
-#include <sys/stat.h> // stat
-#include <unistd.h> // stat
-#include <errno.h> // stat
-#include <stdio.h> // snprintf
-#include <stdint.h>
-
-namespace glim {
-
-class SqliteSession;
-class SqliteQuery;
-
-struct SqliteEx: public std::runtime_error {
- SqliteEx (const std::string& what): std::runtime_error (what) {}
-};
-
-/**
- * The database.
- * According to sqlite3_open <a href="http://sqlite.org/capi3ref.html#sqlite3_open">documentation</a>,
- * only the thread that opened the database can safely access it. This restriction was
- * relaxed, as described in the <a href="http://www.sqlite.org/faq.html#q8">FAQ</a>
- * (the "Is SQLite threadsafe?" question), so we can use the library from multiple
- * threads, but only if no more than one thread at a time accesses the database.
- * This restriction is, in fact, beneficial if the database is used from a single application:
- * by restricting access to a sigle thread at a time, we effectively avoid all deadlock issues.\n
- * This library goals are:\n
- * \li to ensure that SQLite is used in a thread-safe way,
- * \li to provide additional threaded quirks, such as delayed updates (not implemented).
- *
- * The library is targeted at SQLite setup which is \b not \c -DTHREADSAFE,
- * since this is the default setup on UNIX architectures.\n
- * \n
- * This file is a header-only library,
- * whose sole dependencies should be standard STL and posix threading libraries.
- * You can extract this file out of the "glim" library to include it separately in your project.\n
- * \n
- * This library is targeted at UTF-8 API. There is no plans to support the UTF-16 API.\n
- * \n
- * See also:\n
- * \li http://www.sqlite.org/cvstrac/fileview?f=sqlite/src/server.c\n
- * for another way of handling multithreading with SQLite.
- */
-class Sqlite {
- /// No copying allowed.
- Sqlite& operator = (const Sqlite& other) {return *this;}
- /// No copying allowed.
- Sqlite (const Sqlite& other) = delete;
- friend class SqliteSession;
- protected:
- /// Filename the database was opened with; we need it to reopen the database on fork()s.
- /// std::string is used to avoid memory allocation issues.
- std::string filename;
- ::sqlite3* handler;
- ::pthread_mutex_t mutex;
- public:
- /// Flags for the Sqlite constructor.
- enum Flags {
- /**
- * The file will be checked for existence.
- * SqliteEx is thrown if the file is not accessible;
- * format of the error description is "$filename: $strerror".\n
- * Usage example: \code Sqlite db ("filename", Sqlite::existing); \endcode
- */
- existing = 1
- };
- /**
- * Opens the database.
- * @param filename Database filename (UTF-8).
- * @param flags Optional. Currently there is the #existing flag.
- * @throws SqliteEx Thrown if we can't open the database.
- */
- Sqlite (std::string filename, int flags = 0) {
- if (flags & existing) {
- // Check if the file exists already.
- struct stat st; if (stat (filename.c_str(), &st))
- throw SqliteEx (filename + ": " + ::strerror(errno));
- }
- ::pthread_mutex_init (&mutex, NULL);
- this->filename = filename;
- if (::sqlite3_open(filename.c_str(), &handler) != SQLITE_OK)
- throw SqliteEx (std::string("sqlite3_open(") + filename + "): " + ::sqlite3_errmsg(handler));
- }
- /**
- * Closes the database.
- * @throws SqliteEx Thrown if we can't close the database.
- */
- ~Sqlite () {
- ::pthread_mutex_destroy (&mutex);
- if (::sqlite3_close(handler) != SQLITE_OK)
- throw SqliteEx (std::string ("sqlite3_close(): ") + ::sqlite3_errmsg(handler));
- }
-
- Sqlite& exec (const char* query);
- /**
- * Invokes `exec` on `query.c_str()`.
- * Example:\code
- * glim::Sqlite sqlite (":memory:");
- * for (std::string pv: {"page_size = 4096", "secure_delete = 1"}) sqlite->exec2 ("PRAGMA " + pv); \endcode
- */
- template <typename StringLike> Sqlite& exec2 (StringLike query) {return exec (query.c_str());}
-};
-
-/**
- * A single thread session with Sqlite.
- * Only a sigle thread at a time can have an SqliteSession,
- * all other threads will wait, in the SqliteSession constructor,
- * till the active session is either closed or destructed.
- */
-class SqliteSession {
- /// No copying allowed.
- SqliteSession& operator = (const SqliteSession& other) {return *this;}
- /// No copying allowed.
- SqliteSession(SqliteSession& other): db (NULL) {}
- protected:
- Sqlite* db;
- public:
- /**
- * Locks the database.
- * @throws SqliteEx if a mutex error occurs.
- */
- SqliteSession (Sqlite* sqlite): db (sqlite) {
- int err = ::pthread_mutex_lock (&(db->mutex));
- if (err != 0) throw SqliteEx (std::string ("error locking the mutex: ") + ::strerror(err));
- }
- /**
- * A shorter way to construct query from the session.
- * Usage example: \code ses.query(S("create table test (i integer)")).step() \endcode
- * @see SqliteQuery#qstep
- */
- template <typename T>
- SqliteQuery query (T t);
- /// Automatically unlocks the database.
- /// @see close
- ~SqliteSession () {close();}
- /**
- * Unlock the database.
- * It is safe to call this method multiple times.\n
- * You must not use the session after it was closed.\n
- * All resources allocated within this session must be released before the session is closed.
- * @throws SqliteEx if a mutex error occurs.
- */
- void close () {
- if (db == NULL) return;
- int err = ::pthread_mutex_unlock (&(db->mutex));
- db = NULL;
- if (err != 0) throw SqliteEx (std::string ("error unlocking the mutex: ") + ::strerror(err));
- }
- /// True if the \c close method has been already called on this SqliteSession.
- bool isClosed () const {
- return db == NULL;
- }
- /**
- * This class can be used in place of the SQLite handler.
- * Make sure you've released any resources thus manually acquired before this SqliteSession is closed.
- * Usage example:
- * @code
- * glim::Sqlite db (":memory:");
- * glim::SqliteSession ses (&db);
- * sqlite3_exec (ses, "PRAGMA page_size = 4096;", NULL, NULL, NULL);
- * @endcode
- */
- operator ::sqlite3* () const {return db->handler;}
-};
-
-/**
- * Execute the given query, throwing SqliteEx on failure.\n
- * Example:\code
- * glim::Sqlite sqlite (":memory:");
- * sqlite.exec ("PRAGMA page_size = 4096") .exec ("PRAGMA secure_delete = 1"); \endcode
- */
-inline Sqlite& Sqlite::exec (const char* query) {
- SqliteSession ses (this); // Maintains the locks.
- char* errmsg = NULL; ::sqlite3_exec (handler, query, NULL, NULL, &errmsg);
- if (errmsg) throw SqliteEx (std::string ("Sqlite::exec, error in query (") + query + "): " + errmsg);
- return *this;
-}
-
-/**
- * Wraps the sqlite3_stmt; will prepare it, bind values, query and finalize.
- */
-class SqliteQuery {
- protected:
- ::sqlite3_stmt* statement;
- SqliteSession* session;
- int bindCounter;
- /// -1 if statement isn't DONE.
- int mChanges;
- void prepare (SqliteSession* session, char const* query, int queryLength) {
- ::sqlite3* handler = *session;
- if (::sqlite3_prepare_v2 (handler, query, queryLength, &statement, NULL) != SQLITE_OK)
- throw SqliteEx (std::string(query, queryLength) + ": " + ::sqlite3_errmsg(handler));
- }
- /** Shan't copy. */
- SqliteQuery (const SqliteQuery& other) = delete;
- public:
- SqliteQuery (SqliteQuery&& rvalue) {
- statement = rvalue.statement;
- session = rvalue.session;
- bindCounter = rvalue.bindCounter;
- mChanges = rvalue.mChanges;
- rvalue.statement = nullptr;
- }
- /**
- * Prepares the query.
- * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg".
- */
- SqliteQuery (SqliteSession* session, char const* query, int queryLength)
- : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
- prepare (session, query, queryLength);
- }
- /**
- * Prepares the query.
- * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg".
- */
- SqliteQuery (SqliteSession* session, std::pair<char const*, int> query)
- : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
- prepare (session, query.first, query.second);
- }
- /**
- * Prepares the query.
- * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg".
- */
- SqliteQuery (SqliteSession* session, std::string query)
- : statement (NULL), session (session), bindCounter (0), mChanges (-1) {
- prepare (session, query.c_str(), query.length());
- }
- /**
- * Release resources.
- * @see http://sqlite.org/capi3ref.html#sqlite3_finalize
- */
- ~SqliteQuery () {
- if (statement) ::sqlite3_finalize (statement);
- }
-
- /// Call this (followed by the #step) if you need the query to be re-executed.
- /// @see http://sqlite.org/capi3ref.html#sqlite3_reset
- SqliteQuery& reset () {
- bindCounter = 0;
- mChanges = -1;
- ::sqlite3_reset (statement);
- return *this;
- }
-
- /// Synonym for #step.
- bool next () {return step();}
- /**
- * Invokes sqlite3_step.
- * @return \c true if there was a row fetched successfully, \c false if there is no more rows.
- * @see http://sqlite.org/capi3ref.html#sqlite3_step
- */
- bool step () {
- if (mChanges >= 0) {mChanges = 0; return false;}
- int ret = ::sqlite3_step (statement);
- if (ret == SQLITE_ROW) return true;
- if (ret == SQLITE_DONE) {
- mChanges = ::sqlite3_changes (*session);
- return false;
- }
- throw SqliteEx (std::string(::sqlite3_errmsg(*session)));
- }
- /**
- * Perform #step and throw an exception if #step has returned \c false.
- * Usage example:
- * \code (ses.query(S("select count(*) from test where idx = ?")) << 12345).qstep().intAt(1) \endcode
- */
- SqliteQuery& qstep () {
- if (!step())
- throw SqliteEx (std::string("qstep: no rows returned / affected"));
- return *this;
- }
- /**
- * Invokes a DML query and returns the number of rows affected.
- * Example: \code
- * int affected = (ses.query(S("update test set count = count + ? where id = ?")) << 1 << 9).ustep();
- * \endcode
- * @see http://sqlite.org/capi3ref.html#sqlite3_step
- */
- int ustep () {
- int ret = ::sqlite3_step (statement);
- if (ret == SQLITE_DONE) {
- mChanges = ::sqlite3_changes (*session);
- return mChanges;
- }
- if (ret == SQLITE_ROW) return 0;
- throw SqliteEx (std::string(::sqlite3_errmsg(*session)));
- }
-
- /**
- * The number of rows changed by the query.
- * Providing the query was a DML (Data Modification Language),
- * returns the number of rows updated.\n
- * If the query wasn't a DML, returned value is undefined.\n
- * -1 is returned if the query wasn't executed, or after #reset.\n
- * Example: \code
- * SqliteQuery query (&ses, S("update test set count = count + ? where id = ?"));
- * query.bind (1, 1);
- * query.bind (2, 9);
- * query.step ();
- * int affected = query.changes ();
- * \endcode
- * @see #ustep
- */
- int changes () {return mChanges;}
-
- /**
- * The integer value of the given column.
- * @param column 1-based.
- * @see http://sqlite.org/capi3ref.html#sqlite3_column_text
- */
- int intAt (int column) {
- return ::sqlite3_column_int (statement, --column);
- }
-
- /**
- * The integer value of the given column.
- * @param column 1-based.
- * @see http://sqlite.org/capi3ref.html#sqlite3_column_text
- */
- sqlite3_int64 int64at (int column) {
- return ::sqlite3_column_int64 (statement, --column);
- }
-
- /**
- * The floating point number from the given column.
- * @param column 1-based.
- * @see http://sqlite.org/capi3ref.html#sqlite3_column_text
- */
- double doubleAt (int column) {
- return ::sqlite3_column_double (statement, --column);
- }
-
- /**
- * Return the column as UTF-8 characters, which can be used until the next #step.
- * @param column 1-based.
- * @see http://sqlite.org/capi3ref.html#sqlite3_column_text
- */
- std::pair<char const*, int> charsAt (int column) {
- return std::pair<char const*, int> ((char const*) ::sqlite3_column_text (statement, column-1),
- ::sqlite3_column_bytes (statement, column-1));
- }
-
- /**
- * Return the column as C++ string (UTF-8).
- * @param column 1-based.
- */
- std::string stringAt (int column) {
- return std::string ((char const*) ::sqlite3_column_text (statement, column-1),
- ::sqlite3_column_bytes (statement, column-1));
- }
-
- /**
- * The type of the column.
- * SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB or SQLITE_NULL.
- * @param column 1-based.
- * @see http://sqlite.org/capi3ref.html#sqlite3_column_text
- */
- int typeAt (int column) {
- return ::sqlite3_column_type (statement, --column);
- }
-
- /**
- * Binds a value using one of the bind methods.
- */
- template<typename T>
- SqliteQuery& operator << (T value) {
- return bind (++bindCounter, value);
- }
- /**
- * Binds a value using the named parameter and one of the bind methods.
- * @throws SqliteEx if the name could not be found.
- * @see http://sqlite.org/capi3ref.html#sqlite3_bind_parameter_index
- */
- template<typename T>
- SqliteQuery& bind (char const* name, T value) {
- int index = ::sqlite3_bind_parameter_index (statement, name);
- if (index == 0)
- throw SqliteEx (std::string ("No such parameter in the query: ") + name);
- return bind (index, value);
- }
-
- /**
- * Bind a string to the query.
- * @param transient must be true, if lifetime of the string might be shorter than that of the query.
- */
- SqliteQuery& bind (int index, const char* text, int length, bool transient = false) {
- if (::sqlite3_bind_text (statement, index, text, length,
- transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
- throw SqliteEx (std::string (::sqlite3_errmsg (*session)));
- return *this;
- }
- /**
- * Bind a string to the query.
- * @param transient must be true, if lifetime of the string might be shorter than that of the query.
- */
- SqliteQuery& bind (int index, std::pair<const char*, int> text, bool transient = false) {
- if (::sqlite3_bind_text (statement, index, text.first, text.second,
- transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
- throw SqliteEx (std::string (::sqlite3_errmsg (*session)));
- return *this;
- }
- /**
- * Bind a string to the query.
- * @param transient must be true, if lifetime of the string might be shorter than that of the query.
- */
- SqliteQuery& bind (int index, const std::string& text, bool transient = true) {
- if (::sqlite3_bind_text (statement, index, text.data(), text.length(),
- transient ? SQLITE_TRANSIENT : SQLITE_STATIC) != SQLITE_OK)
- throw SqliteEx (std::string (::sqlite3_errmsg (*session)));
- return *this;
- }
- /**
- * Bind an integer to the query.
- */
- SqliteQuery& bind (int index, int value) {
- if (::sqlite3_bind_int (statement, index, value) != SQLITE_OK)
- throw SqliteEx (std::string (::sqlite3_errmsg (*session)));
- return *this;
- }
- /**
- * Bind an 64-bit integer to the query.
- */
- SqliteQuery& bind (int index, sqlite3_int64 value) {
- if (::sqlite3_bind_int64 (statement, index, value) != SQLITE_OK)
- throw SqliteEx (std::string (::sqlite3_errmsg (*session)));
- return *this;
- }
-};
-
-/**
- * Version of SqliteQuery suitable for using SQLite in parallel with other processes.
- * Will automatically handle the SQLITE_SCHEMA error
- * and will automatically repeat attempts after SQLITE_BUSY,
- * but it requires that the query string supplied
- * is constant and available during the SqliteParQuery lifetime.
- * Error messages, contained in exceptions, may differ from SqliteQuery by containing the query
- * (for example, the #step method will throw "$query: $errmsg" instead of just "$errmsg").
- */
-class SqliteParQuery: public SqliteQuery {
- protected:
- char const* query;
- int queryLength;
- int repeat;
- int wait;
- public:
- /**
- * Prepares the query.
- * @param repeat the number of times we try to repeat the query when SQLITE_BUSY is returned.
- * @param wait how long, in milliseconds (1/1000 of a second) we are to wait before repeating.
- * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg".
- */
- SqliteParQuery (SqliteSession* session, char const* query, int queryLength, int repeat = 90, int wait = 20)
- : SqliteQuery (session, query, queryLength) {
- this->query = query;
- this->queryLength = queryLength;
- this->repeat = repeat;
- this->wait = wait;
- }
- /**
- * Prepares the query.
- * @param query the SQL query together with its length.
- * @param repeat the number of times we try to repeat the query when SQLITE_BUSY is returned.
- * @param wait how long, in milliseconds (1/1000 of a second) we are to wait before repeating.
- * @throws SqliteEx if sqlite3_prepare fails; format of the error message is "$query: $errmsg".
- */
- SqliteParQuery (SqliteSession* session, std::pair<char const*, int> query, int repeat = 90, int wait = 20)
- : SqliteQuery (session, query) {
- this->query = query.first;
- this->queryLength = query.second;
- this->repeat = repeat;
- this->wait = wait;
- }
-
- bool next () {return step();}
- bool step () {
- if (mChanges >= 0) {mChanges = 0; return false;}
- repeat:
- int ret = ::sqlite3_step (statement);
- if (ret == SQLITE_ROW) return true;
- if (ret == SQLITE_DONE) {
- mChanges = ::sqlite3_changes (*session);
- return false;
- }
- if (ret == SQLITE_SCHEMA) {
- ::sqlite3_stmt* old = statement;
- prepare (session, query, queryLength);
- ::sqlite3_transfer_bindings(old, statement);
- ::sqlite3_finalize (old);
- goto repeat;
- }
- if (ret == SQLITE_BUSY) for (int repeat = this->repeat; ret == SQLITE_BUSY && repeat >= 0; --repeat) {
- //struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = wait * 1000000; // nan is 10^-9 of sec.
- //while (::nanosleep (&ts, &ts) == EINTR);
- ::sqlite3_sleep (wait);
- ret = ::sqlite3_step (statement);
- }
- throw SqliteEx (std::string(query, queryLength) + ::sqlite3_errmsg(*session));
- }
-};
-
-template <typename T>
-SqliteQuery SqliteSession::query (T t) {
- return SqliteQuery (this, t);
-}
-
-}; // namespace glim
-
-#endif // GLIM_SQLITE_HPP_