diff options
author | rtk0c <[email protected]> | 2022-05-22 14:54:06 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-05-22 14:54:06 -0700 |
commit | 55c4420b69fa172ac7b6b3523597b9a46a5d1bcd (patch) | |
tree | 407fbd2fd7911b8bfa8567502d7493708036be2f /3rdparty/sqlitecpp/source | |
parent | 195e30b1e39fa4dc535172ba248f0c18733dcb3c (diff) |
Vendor SQLiteCpp completely for internal patching
Diffstat (limited to '3rdparty/sqlitecpp/source')
19 files changed, 3537 insertions, 0 deletions
diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Assertion.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Assertion.h new file mode 100644 index 0000000..b6d00be --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Assertion.h @@ -0,0 +1,46 @@ +/** + * @file Assertion.h + * @ingroup SQLiteCpp + * @brief Definition of the SQLITECPP_ASSERT() macro. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <cassert> + + +/** + * SQLITECPP_ASSERT SQLITECPP_ASSERT() is used in destructors, where exceptions shall not be thrown + * + * Define SQLITECPP_ENABLE_ASSERT_HANDLER at the project level + * and define a SQLite::assertion_failed() assertion handler + * to tell SQLiteC++ to use it instead of assert() when an assertion fail. +*/ +#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER + +// if an assert handler is provided by user code, use it instead of assert() +namespace SQLite +{ + // declaration of the assert handler to define in user code + void assertion_failed(const char* apFile, const long apLine, const char* apFunc, + const char* apExpr, const char* apMsg); + +#ifdef _MSC_VER + #define __func__ __FUNCTION__ +#endif +// call the assert handler provided by user code +#define SQLITECPP_ASSERT(expression, message) \ + if (!(expression)) SQLite::assertion_failed(__FILE__, __LINE__, __func__, #expression, message) +} // namespace SQLite + +#else + +// if no assert handler provided by user code, use standard assert() +// (note: in release mode assert() does nothing) +#define SQLITECPP_ASSERT(expression, message) assert(expression && message) + +#endif diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.cpp new file mode 100644 index 0000000..183d314 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.cpp @@ -0,0 +1,83 @@ +/** + * @file Backup.cpp + * @ingroup SQLiteCpp + * @brief Backup is used to backup a database file in a safe and online way. + * + * Copyright (c) 2015 Shibao HONG ([email protected]) + * Copyright (c) 2015-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Backup.h> + +#include <SQLiteCpp/Exception.h> + +#include <sqlite3.h> + +namespace SQLite +{ + +// Initialize resource for SQLite database backup +Backup::Backup(Database& aDestDatabase, + const char* apDestDatabaseName, + Database& aSrcDatabase, + const char* apSrcDatabaseName) +{ + mpSQLiteBackup = sqlite3_backup_init(aDestDatabase.getHandle(), + apDestDatabaseName, + aSrcDatabase.getHandle(), + apSrcDatabaseName); + if (nullptr == mpSQLiteBackup) + { + // If an error occurs, the error code and message are attached to the destination database connection. + throw SQLite::Exception(aDestDatabase.getHandle()); + } +} + +Backup::Backup(Database& aDestDatabase, + const std::string& aDestDatabaseName, + Database& aSrcDatabase, + const std::string& aSrcDatabaseName) : + Backup(aDestDatabase, aDestDatabaseName.c_str(), aSrcDatabase, aSrcDatabaseName.c_str()) +{ +} + +Backup::Backup(Database &aDestDatabase, Database &aSrcDatabase) : + Backup(aDestDatabase, "main", aSrcDatabase, "main") +{ +} + +// Release resource for SQLite database backup +Backup::~Backup() +{ + if (mpSQLiteBackup) + { + sqlite3_backup_finish(mpSQLiteBackup); + } +} + +// Execute backup step with a given number of source pages to be copied +int Backup::executeStep(const int aNumPage /* = -1 */) +{ + const int res = sqlite3_backup_step(mpSQLiteBackup, aNumPage); + if (SQLITE_OK != res && SQLITE_DONE != res && SQLITE_BUSY != res && SQLITE_LOCKED != res) + { + throw SQLite::Exception(sqlite3_errstr(res), res); + } + return res; +} + +// Get the number of remaining source pages to be copied in this backup process +int Backup::getRemainingPageCount() +{ + return sqlite3_backup_remaining(mpSQLiteBackup); +} + +// Get the number of total source pages to be copied in this backup process +int Backup::getTotalPageCount() +{ + return sqlite3_backup_pagecount(mpSQLiteBackup); +} + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.h new file mode 100644 index 0000000..21ad662 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Backup.h @@ -0,0 +1,127 @@ +/** + * @file Backup.h + * @ingroup SQLiteCpp + * @brief Backup is used to backup a database file in a safe and online way. + * + * Copyright (c) 2015 Shibao HONG ([email protected]) + * Copyright (c) 2015-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Database.h> + +#include <string> + +// Forward declaration to avoid inclusion of <sqlite3.h> in a header +struct sqlite3_backup; + +namespace SQLite +{ + +/** + * @brief RAII encapsulation of a SQLite Database Backup process. + * + * A Backup object is used to backup a source database file to a destination database file + * in a safe and online way. + * + * See also the a reference implementation of live backup taken from the official site: + * https://www.sqlite.org/backup.html + */ +class Backup +{ +public: + /** + * @brief Initialize a SQLite Backup object. + * + * Initialize a SQLite Backup object for the source database and destination database. + * The database name is "main" for the main database, "temp" for the temporary database, + * or the name specified after the AS keyword in an ATTACH statement for an attached database. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] apDestDatabaseName Destination database name + * @param[in] aSrcDatabase Source database connection + * @param[in] apSrcDatabaseName Source database name + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + const char* apDestDatabaseName, + Database& aSrcDatabase, + const char* apSrcDatabaseName); + + /** + * @brief Initialize a SQLite Backup object. + * + * Initialize a SQLite Backup object for source database and destination database. + * The database name is "main" for the main database, "temp" for the temporary database, + * or the name specified after the AS keyword in an ATTACH statement for an attached database. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] aDestDatabaseName Destination database name + * @param[in] aSrcDatabase Source database connection + * @param[in] aSrcDatabaseName Source database name + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + const std::string& aDestDatabaseName, + Database& aSrcDatabase, + const std::string& aSrcDatabaseName); + + /** + * @brief Initialize a SQLite Backup object for main databases. + * + * Initialize a SQLite Backup object for source database and destination database. + * Backup the main databases between the source and the destination. + * + * Exception is thrown in case of error, then the Backup object is NOT constructed. + * + * @param[in] aDestDatabase Destination database connection + * @param[in] aSrcDatabase Source database connection + * + * @throw SQLite::Exception in case of error + */ + Backup(Database& aDestDatabase, + Database& aSrcDatabase); + + // Backup is non-copyable + Backup(const Backup&) = delete; + Backup& operator=(const Backup&) = delete; + + /// Release the SQLite Backup resource. + ~Backup(); + + /** + * @brief Execute a step of backup with a given number of source pages to be copied + * + * Exception is thrown when SQLITE_IOERR_XXX, SQLITE_NOMEM, or SQLITE_READONLY is returned + * in sqlite3_backup_step(). These errors are considered fatal, so there is no point + * in retrying the call to executeStep(). + * + * @param[in] aNumPage The number of source pages to be copied, with a negative value meaning all remaining source pages + * + * @return SQLITE_OK/SQLITE_DONE/SQLITE_BUSY/SQLITE_LOCKED + * + * @throw SQLite::Exception in case of error + */ + int executeStep(const int aNumPage = -1); + + /// Return the number of source pages still to be backed up as of the most recent call to executeStep(). + int getRemainingPageCount(); + + /// Return the total number of pages in the source database as of the most recent call to executeStep(). + int getTotalPageCount(); + +private: + // TODO: use std::unique_ptr with a custom deleter to call sqlite3_backup_finish() + sqlite3_backup* mpSQLiteBackup = nullptr; ///< Pointer to SQLite Database Backup Handle +}; + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Column.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Column.cpp new file mode 100644 index 0000000..f5dc0d9 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Column.cpp @@ -0,0 +1,122 @@ +/** + * @file Column.cpp + * @ingroup SQLiteCpp + * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Column.h> + +#include <sqlite3.h> + +#include <iostream> + + +namespace SQLite +{ + +const int INTEGER = SQLITE_INTEGER; +const int FLOAT = SQLITE_FLOAT; +const int TEXT = SQLITE_TEXT; +const int BLOB = SQLITE_BLOB; +const int Null = SQLITE_NULL; + + +// Encapsulation of a Column in a row of the result pointed by the prepared Statement. +Column::Column(const Statement::TStatementPtr& aStmtPtr, int aIndex) : + mStmtPtr(aStmtPtr), + mIndex(aIndex) +{ + if (!aStmtPtr) + { + throw SQLite::Exception("Statement was destroyed"); + } +} + +// Return the named assigned to this result column (potentially aliased) +const char* Column::getName() const noexcept +{ + return sqlite3_column_name(mStmtPtr.get(), mIndex); +} + +#ifdef SQLITE_ENABLE_COLUMN_METADATA +// Return the name of the table column that is the origin of this result column +const char* Column::getOriginName() const noexcept +{ + return sqlite3_column_origin_name(mStmtPtr.get(), mIndex); +} +#endif + +// Return the integer value of the column specified by its index starting at 0 +int Column::getInt() const noexcept +{ + return sqlite3_column_int(mStmtPtr.get(), mIndex); +} + +// Return the unsigned integer value of the column specified by its index starting at 0 +unsigned Column::getUInt() const noexcept +{ + return static_cast<unsigned>(getInt64()); +} + +// Return the 64bits integer value of the column specified by its index starting at 0 +long long Column::getInt64() const noexcept +{ + return sqlite3_column_int64(mStmtPtr.get(), mIndex); +} + +// Return the double value of the column specified by its index starting at 0 +double Column::getDouble() const noexcept +{ + return sqlite3_column_double(mStmtPtr.get(), mIndex); +} + +// Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0 +const char* Column::getText(const char* apDefaultValue /* = "" */) const noexcept +{ + auto pText = reinterpret_cast<const char*>(sqlite3_column_text(mStmtPtr.get(), mIndex)); + return (pText?pText:apDefaultValue); +} + +// Return a pointer to the blob value (*not* NULL terminated) of the column specified by its index starting at 0 +const void* Column::getBlob() const noexcept +{ + return sqlite3_column_blob(mStmtPtr.get(), mIndex); +} + +// Return a std::string to a TEXT or BLOB column +std::string Column::getString() const +{ + // Note: using sqlite3_column_blob and not sqlite3_column_text + // - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly + auto data = static_cast<const char *>(sqlite3_column_blob(mStmtPtr.get(), mIndex)); + + // SQLite docs: "The safest policy is to invoke⦠sqlite3_column_blob() followed by sqlite3_column_bytes()" + // Note: std::string is ok to pass nullptr as first arg, if length is 0 + return std::string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex)); +} + +// Return the type of the value of the column +int Column::getType() const noexcept +{ + return sqlite3_column_type(mStmtPtr.get(), mIndex); +} + +// Return the number of bytes used by the text value of the column +int Column::getBytes() const noexcept +{ + return sqlite3_column_bytes(mStmtPtr.get(), mIndex); +} + +// Standard std::ostream inserter +std::ostream& operator<<(std::ostream& aStream, const Column& aColumn) +{ + aStream.write(aColumn.getText(), aColumn.getBytes()); + return aStream; +} + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Column.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Column.h new file mode 100644 index 0000000..bc349f9 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Column.h @@ -0,0 +1,291 @@ +/** + * @file Column.h + * @ingroup SQLiteCpp + * @brief Encapsulation of a Column in a row of the result pointed by the prepared SQLite::Statement. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Statement.h> +#include <SQLiteCpp/Exception.h> + +#include <string> +#include <memory> +#include <climits> // For INT_MAX + +// Forward declarations to avoid inclusion of <sqlite3.h> in a header +struct sqlite3_stmt; + +namespace SQLite +{ + +extern const int INTEGER; ///< SQLITE_INTEGER +extern const int FLOAT; ///< SQLITE_FLOAT +extern const int TEXT; ///< SQLITE_TEXT +extern const int BLOB; ///< SQLITE_BLOB +extern const int Null; ///< SQLITE_NULL + +/** + * @brief Encapsulation of a Column in a row of the result pointed by the prepared Statement. + * + * A Column is a particular field of SQLite data in the current row of result + * of the Statement : it points to a single cell. + * + * Its value can be expressed as a text, and, when applicable, as a numeric + * (integer or floating point) or a binary blob. + * + * Thread-safety: a Column object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class Column +{ +public: + /** + * @brief Encapsulation of a Column in a Row of the result. + * + * @param[in] aStmtPtr Shared pointer to the prepared SQLite Statement Object. + * @param[in] aIndex Index of the column in the row of result, starting at 0 + */ + explicit Column(const Statement::TStatementPtr& aStmtPtr, int aIndex); + + // default destructor: the finalization will be done by the destructor of the last shared pointer + // default copy constructor and assignment operator are perfectly suited : + // they copy the Statement::Ptr which in turn increments the reference counter. + + /** + * @brief Return a pointer to the named assigned to this result column (potentially aliased) + * + * @see getOriginName() to get original column name (not aliased) + */ + const char* getName() const noexcept; + +#ifdef SQLITE_ENABLE_COLUMN_METADATA + /** + * @brief Return a pointer to the table column name that is the origin of this result column + * + * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : + * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), + * - and also when compiling this wrapper. + */ + const char* getOriginName() const noexcept; +#endif + + /// Return the integer value of the column. + int getInt() const noexcept; + /// Return the 32bits unsigned integer value of the column (note that SQLite3 does not support unsigned 64bits). + unsigned getUInt() const noexcept; + /// Return the 64bits integer value of the column (note that SQLite3 does not support unsigned 64bits). + long long getInt64() const noexcept; + /// Return the double (64bits float) value of the column + double getDouble() const noexcept; + /** + * @brief Return a pointer to the text value (NULL terminated string) of the column. + * + * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), + * thus you must copy it before using it beyond its scope (to a std::string for instance). + */ + const char* getText(const char* apDefaultValue = "") const noexcept; + /** + * @brief Return a pointer to the binary blob value of the column. + * + * @warning The value pointed at is only valid while the statement is valid (ie. not finalized), + * thus you must copy it before using it beyond its scope (to a std::string for instance). + */ + const void* getBlob() const noexcept; + /** + * @brief Return a std::string for a TEXT or BLOB column. + * + * Note this correctly handles strings that contain null bytes. + */ + std::string getString() const; + + /** + * @brief Return the type of the value of the column + * + * Return either SQLite::INTEGER, SQLite::FLOAT, SQLite::TEXT, SQLite::BLOB, or SQLite::Null. + * + * @warning After a type conversion (by a call to a getXxx on a Column of a Yyy type), + * the value returned by sqlite3_column_type() is undefined. + */ + int getType() const noexcept; + + /// Test if the column is an integer type value (meaningful only before any conversion) + bool isInteger() const noexcept + { + return (SQLite::INTEGER == getType()); + } + /// Test if the column is a floating point type value (meaningful only before any conversion) + bool isFloat() const noexcept + { + return (SQLite::FLOAT == getType()); + } + /// Test if the column is a text type value (meaningful only before any conversion) + bool isText() const noexcept + { + return (SQLite::TEXT == getType()); + } + /// Test if the column is a binary blob type value (meaningful only before any conversion) + bool isBlob() const noexcept + { + return (SQLite::BLOB == getType()); + } + /// Test if the column is NULL (meaningful only before any conversion) + bool isNull() const noexcept + { + return (SQLite::Null == getType()); + } + + /** + * @brief Return the number of bytes used by the text (or blob) value of the column + * + * Return either : + * - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator + * - size in bytes of the string representation of the numerical value (integer or double) + * - size in bytes of the binary blob returned by getBlob() + * - 0 for a NULL value + */ + int getBytes() const noexcept; + + /// Alias returning the number of bytes used by the text (or blob) value of the column + int size() const noexcept + { + return getBytes (); + } + + /// Inline cast operator to char + operator char() const + { + return static_cast<char>(getInt()); + } + /// Inline cast operator to unsigned char + operator unsigned char() const + { + return static_cast<unsigned char>(getInt()); + } + /// Inline cast operator to short + operator short() const + { + return static_cast<short>(getInt()); + } + /// Inline cast operator to unsigned short + operator unsigned short() const + { + return static_cast<unsigned short>(getInt()); + } + + /// Inline cast operator to int + operator int() const + { + return getInt(); + } + /// Inline cast operator to 32bits unsigned integer + operator unsigned int() const + { + return getUInt(); + } +#if (LONG_MAX == INT_MAX) // 4 bytes "long" type means the data model is ILP32 or LLP64 (Win64 Visual C++ and MinGW) + /// Inline cast operator to 32bits long + operator long() const + { + return getInt(); + } + /// Inline cast operator to 32bits unsigned long + operator unsigned long() const + { + return getUInt(); + } +#else // 8 bytes "long" type means the data model is LP64 (Most Unix-like, Windows when using Cygwin; z/OS) + /// Inline cast operator to 64bits long when the data model of the system is LP64 (Linux 64 bits...) + operator long() const + { + return getInt64(); + } +#endif + + /// Inline cast operator to 64bits integer + operator long long() const + { + return getInt64(); + } + /// Inline cast operator to double + operator double() const + { + return getDouble(); + } + /** + * @brief Inline cast operator to char* + * + * @see getText + */ + operator const char*() const + { + return getText(); + } + /** + * @brief Inline cast operator to void* + * + * @see getBlob + */ + operator const void*() const + { + return getBlob(); + } + + /** + * @brief Inline cast operator to std::string + * + * Handles BLOB or TEXT, which may contain null bytes within + * + * @see getString + */ + operator std::string() const + { + return getString(); + } + +private: + Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object + int mIndex; ///< Index of the column in the row of result, starting at 0 +}; + +/** + * @brief Standard std::ostream text inserter + * + * Insert the text value of the Column object, using getText(), into the provided stream. + * + * @param[in] aStream Stream to use + * @param[in] aColumn Column object to insert into the provided stream + * + * @return Reference to the stream used + */ +std::ostream& operator<<(std::ostream& aStream, const Column& aColumn); + +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 + +// Create an instance of T from the first N columns, see declaration in Statement.h for full details +template<typename T, int N> +T Statement::getColumns() +{ + checkRow(); + checkIndex(N - 1); + return getColumns<T>(std::make_integer_sequence<int, N>{}); +} + +// Helper function called by getColums<typename T, int N> +template<typename T, const int... Is> +T Statement::getColumns(const std::integer_sequence<int, Is...>) +{ + return T{Column(mpPreparedStatement, Is)...}; +} + +#endif + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp new file mode 100644 index 0000000..eb76c88 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp @@ -0,0 +1,452 @@ +/** + * @file Database.cpp + * @ingroup SQLiteCpp + * @brief Management of a SQLite Database Connection. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Database.h> + +#include <SQLiteCpp/Assertion.h> +#include <SQLiteCpp/Backup.h> +#include <SQLiteCpp/Exception.h> +#include <SQLiteCpp/Statement.h> + +#include <sqlite3.h> +#include <fstream> +#include <string.h> + +#ifndef SQLITE_DETERMINISTIC +#define SQLITE_DETERMINISTIC 0x800 +#endif // SQLITE_DETERMINISTIC + + +namespace SQLite +{ + +const int OPEN_READONLY = SQLITE_OPEN_READONLY; +const int OPEN_READWRITE = SQLITE_OPEN_READWRITE; +const int OPEN_CREATE = SQLITE_OPEN_CREATE; +const int OPEN_URI = SQLITE_OPEN_URI; +const int OPEN_MEMORY = SQLITE_OPEN_MEMORY; +const int OPEN_NOMUTEX = SQLITE_OPEN_NOMUTEX; +const int OPEN_FULLMUTEX = SQLITE_OPEN_FULLMUTEX; +const int OPEN_SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE; +const int OPEN_PRIVATECACHE = SQLITE_OPEN_PRIVATECACHE; +#if SQLITE_VERSION_NUMBER >= 3031000 +const int OPEN_NOFOLLOW = SQLITE_OPEN_NOFOLLOW; +#else +const int OPEN_NOFOLLOW = 0; +#endif + +const int OK = SQLITE_OK; + +const char* VERSION = SQLITE_VERSION; +const int VERSION_NUMBER = SQLITE_VERSION_NUMBER; + +// Return SQLite version string using runtime call to the compiled library +const char* getLibVersion() noexcept +{ + return sqlite3_libversion(); +} + +// Return SQLite version number using runtime call to the compiled library +int getLibVersionNumber() noexcept +{ + return sqlite3_libversion_number(); +} + + +// Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags. +Database::Database(const char* apFilename, + const int aFlags /* = SQLite::OPEN_READONLY*/, + const int aBusyTimeoutMs /* = 0 */, + const char* apVfs /* = nullptr*/) : + mFilename(apFilename) +{ + sqlite3* handle; + const int ret = sqlite3_open_v2(apFilename, &handle, aFlags, apVfs); + mSQLitePtr.reset(handle); + if (SQLITE_OK != ret) + { + throw SQLite::Exception(handle, ret); + } + if (aBusyTimeoutMs > 0) + { + setBusyTimeout(aBusyTimeoutMs); + } +} + +// Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. +void Database::Deleter::operator()(sqlite3* apSQLite) +{ + const int ret = sqlite3_close(apSQLite); // Calling sqlite3_close() with a nullptr argument is a harmless no-op. + + // Avoid unreferenced variable warning when build in release mode + (void) ret; + + // Only case of error is SQLITE_BUSY: "database is locked" (some statements are not finalized) + // Never throw an exception in a destructor : + SQLITECPP_ASSERT(SQLITE_OK == ret, "database is locked"); // See SQLITECPP_ENABLE_ASSERT_HANDLER +} + +/** + * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. + * + * This is useful in multithreaded program to handle case where a table is locked for writting by a thread. + * Any other thread cannot access the table and will receive a SQLITE_BUSY error: + * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. + * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". + * Default busy timeout is 0ms. + * + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY + * + * @throw SQLite::Exception in case of error + */ +void Database::setBusyTimeout(const int aBusyTimeoutMs) +{ + const int ret = sqlite3_busy_timeout(getHandle(), aBusyTimeoutMs); + check(ret); +} + +// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...). +// Return the number of changes. +int Database::exec(const char* apQueries) +{ + const int ret = tryExec(apQueries); + check(ret); + + // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only) + return sqlite3_changes(getHandle()); +} + +int Database::tryExec(const char* apQueries) noexcept +{ + return sqlite3_exec(getHandle(), apQueries, nullptr, nullptr, nullptr); +} + +// Shortcut to execute a one step query and fetch the first column of the result. +// WARNING: Be very careful with this dangerous method: you have to +// make a COPY OF THE result, else it will be destroy before the next line +// (when the underlying temporary Statement and Column objects are destroyed) +// this is an issue only for pointer type result (ie. char* and blob) +// (use the Column copy-constructor) +Column Database::execAndGet(const char* apQuery) +{ + Statement query(*this, apQuery); + (void)query.executeStep(); // Can return false if no result, which will throw next line in getColumn() + return query.getColumn(0); +} + +// Shortcut to test if a table exists. +bool Database::tableExists(const char* apTableName) +{ + Statement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?"); + query.bind(1, apTableName); + (void)query.executeStep(); // Cannot return false, as the above query always return a result + return (1 == query.getColumn(0).getInt()); +} + +// Get the rowid of the most recent successful INSERT into the database from the current connection. +long long Database::getLastInsertRowid() const noexcept +{ + return sqlite3_last_insert_rowid(getHandle()); +} + +// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). +int Database::getChanges() const noexcept +{ + return sqlite3_changes(getHandle()); +} + +// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection. +int Database::getTotalChanges() const noexcept +{ + return sqlite3_total_changes(getHandle()); +} + +// Return the numeric result code for the most recent failed API call (if any). +int Database::getErrorCode() const noexcept +{ + return sqlite3_errcode(getHandle()); +} + +// Return the extended numeric result code for the most recent failed API call (if any). +int Database::getExtendedErrorCode() const noexcept +{ + return sqlite3_extended_errcode(getHandle()); +} + +// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). +const char* Database::getErrorMsg() const noexcept +{ + return sqlite3_errmsg(getHandle()); +} + +// Attach a custom function to your sqlite database. Assumes UTF8 text representation. +// Parameter details can be found here: http://www.sqlite.org/c3ref/create_function.html +void Database::createFunction(const char* apFuncName, + int aNbArg, + bool abDeterministic, + void* apApp, + void (*apFunc)(sqlite3_context *, int, sqlite3_value **), + void (*apStep)(sqlite3_context *, int, sqlite3_value **) /* = nullptr */, + void (*apFinal)(sqlite3_context *) /* = nullptr */, // NOLINT(readability/casting) + void (*apDestroy)(void *) /* = nullptr */) +{ + int textRep = SQLITE_UTF8; + // optimization if deterministic function (e.g. of nondeterministic function random()) + if (abDeterministic) + { + textRep = textRep | SQLITE_DETERMINISTIC; + } + const int ret = sqlite3_create_function_v2(getHandle(), apFuncName, aNbArg, textRep, + apApp, apFunc, apStep, apFinal, apDestroy); + check(ret); +} + +// Load an extension into the sqlite database. Only affects the current connection. +// Parameter details can be found here: http://www.sqlite.org/c3ref/load_extension.html +void Database::loadExtension(const char* apExtensionName, const char *apEntryPointName) +{ +#ifdef SQLITE_OMIT_LOAD_EXTENSION + // Unused + (void)apExtensionName; + (void)apEntryPointName; + + throw SQLite::Exception("sqlite extensions are disabled"); +#else +#ifdef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION // Since SQLite 3.13 (2016-05-18): + // Security warning: + // It is recommended that the SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION method be used to enable only this interface. + // The use of the sqlite3_enable_load_extension() interface should be avoided to keep the SQL load_extension() + // disabled and prevent SQL injections from giving attackers access to extension loading capabilities. + // (NOTE: not using nullptr: cannot pass object of non-POD type 'std::__1::nullptr_t' through variadic function) + int ret = sqlite3_db_config(getHandle(), SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); // NOTE: not using nullptr +#else + int ret = sqlite3_enable_load_extension(getHandle(), 1); +#endif + check(ret); + + ret = sqlite3_load_extension(getHandle(), apExtensionName, apEntryPointName, 0); + check(ret); +#endif +} + +// Set the key for the current sqlite database instance. +void Database::key(const std::string& aKey) const +{ + int passLen = static_cast<int>(aKey.length()); +#ifdef SQLITE_HAS_CODEC + if (passLen > 0) + { + const int ret = sqlite3_key(getHandle(), aKey.c_str(), passLen); + check(ret); + } +#else // SQLITE_HAS_CODEC + if (passLen > 0) + { + throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); + } +#endif // SQLITE_HAS_CODEC +} + +// Reset the key for the current sqlite database instance. +void Database::rekey(const std::string& aNewKey) const +{ +#ifdef SQLITE_HAS_CODEC + int passLen = aNewKey.length(); + if (passLen > 0) + { + const int ret = sqlite3_rekey(getHandle(), aNewKey.c_str(), passLen); + check(ret); + } + else + { + const int ret = sqlite3_rekey(getHandle(), nullptr, 0); + check(ret); + } +#else // SQLITE_HAS_CODEC + static_cast<void>(aNewKey); // silence unused parameter warning + throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable."); +#endif // SQLITE_HAS_CODEC +} + +// Test if a file contains an unencrypted database. +bool Database::isUnencrypted(const std::string& aFilename) +{ + if (aFilename.empty()) + { + throw SQLite::Exception("Could not open database, the aFilename parameter was empty."); + } + + std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); + char header[16]; + if (fileBuffer.is_open()) + { + fileBuffer.seekg(0, std::ios::beg); + fileBuffer.getline(header, 16); + fileBuffer.close(); + } + else + { + throw SQLite::Exception("Error opening file: " + aFilename); + } + + return strncmp(header, "SQLite format 3\000", 16) == 0; +} + +// Parse header data from a database. +Header Database::getHeaderInfo(const std::string& aFilename) +{ + Header h; + unsigned char buf[100]; + char* pBuf = reinterpret_cast<char*>(&buf[0]); + char* pHeaderStr = reinterpret_cast<char*>(&h.headerStr[0]); + + if (aFilename.empty()) + { + throw SQLite::Exception("Filename parameter is empty"); + } + + { + std::ifstream fileBuffer(aFilename.c_str(), std::ios::in | std::ios::binary); + if (fileBuffer.is_open()) + { + fileBuffer.seekg(0, std::ios::beg); + fileBuffer.read(pBuf, 100); + fileBuffer.close(); + if (fileBuffer.gcount() < 100) + { + throw SQLite::Exception("File " + aFilename + " is too short"); + } + } + else + { + throw SQLite::Exception("Error opening file " + aFilename); + } + } + + // If the "magic string" can't be found then header is invalid, corrupt or unreadable + memcpy(pHeaderStr, pBuf, 16); + pHeaderStr[15] = '\0'; + if (strncmp(pHeaderStr, "SQLite format 3", 15) != 0) + { + throw SQLite::Exception("Invalid or encrypted SQLite header in file " + aFilename); + } + + h.pageSizeBytes = (buf[16] << 8) | buf[17]; + h.fileFormatWriteVersion = buf[18]; + h.fileFormatReadVersion = buf[19]; + h.reservedSpaceBytes = buf[20]; + h.maxEmbeddedPayloadFrac = buf[21]; + h.minEmbeddedPayloadFrac = buf[22]; + h.leafPayloadFrac = buf[23]; + + h.fileChangeCounter = + (buf[24] << 24) | + (buf[25] << 16) | + (buf[26] << 8) | + (buf[27] << 0); + + h.databaseSizePages = + (buf[28] << 24) | + (buf[29] << 16) | + (buf[30] << 8) | + (buf[31] << 0); + + h.firstFreelistTrunkPage = + (buf[32] << 24) | + (buf[33] << 16) | + (buf[34] << 8) | + (buf[35] << 0); + + h.totalFreelistPages = + (buf[36] << 24) | + (buf[37] << 16) | + (buf[38] << 8) | + (buf[39] << 0); + + h.schemaCookie = + (buf[40] << 24) | + (buf[41] << 16) | + (buf[42] << 8) | + (buf[43] << 0); + + h.schemaFormatNumber = + (buf[44] << 24) | + (buf[45] << 16) | + (buf[46] << 8) | + (buf[47] << 0); + + h.defaultPageCacheSizeBytes = + (buf[48] << 24) | + (buf[49] << 16) | + (buf[50] << 8) | + (buf[51] << 0); + + h.largestBTreePageNumber = + (buf[52] << 24) | + (buf[53] << 16) | + (buf[54] << 8) | + (buf[55] << 0); + + h.databaseTextEncoding = + (buf[56] << 24) | + (buf[57] << 16) | + (buf[58] << 8) | + (buf[59] << 0); + + h.userVersion = + (buf[60] << 24) | + (buf[61] << 16) | + (buf[62] << 8) | + (buf[63] << 0); + + h.incrementalVaccumMode = + (buf[64] << 24) | + (buf[65] << 16) | + (buf[66] << 8) | + (buf[67] << 0); + + h.applicationId = + (buf[68] << 24) | + (buf[69] << 16) | + (buf[70] << 8) | + (buf[71] << 0); + + h.versionValidFor = + (buf[92] << 24) | + (buf[93] << 16) | + (buf[94] << 8) | + (buf[95] << 0); + + h.sqliteVersion = + (buf[96] << 24) | + (buf[97] << 16) | + (buf[98] << 8) | + (buf[99] << 0); + + return h; +} + +void Database::backup(const char* apFilename, BackupType aType) +{ + // Open the database file identified by apFilename + Database otherDatabase(apFilename, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); + + // For a 'Save' operation, data is copied from the current Database to the other. A 'Load' is the reverse. + Database& src = (aType == Save ? *this : otherDatabase); + Database& dest = (aType == Save ? otherDatabase : *this); + + // Set up the backup procedure to copy between the "main" databases of each connection + Backup bkp(dest, src); + bkp.executeStep(); // Execute all steps at once + + // RAII Finish Backup an Close the other Database +} + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Database.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Database.h new file mode 100644 index 0000000..7ee45d8 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Database.h @@ -0,0 +1,594 @@ +/** + * @file Database.h + * @ingroup SQLiteCpp + * @brief Management of a SQLite Database Connection. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Column.h> + +// c++17: MinGW GCC version > 8 +// c++17: Visual Studio 2017 version 15.7 +// c++17: macOS unless targetting compatibility with macOS < 10.15 +#if __cplusplus >= 201703L + #if defined(__MINGW32__) || defined(__MINGW64__) + #if __GNUC__ > 8 // MinGW requires GCC version > 8 for std::filesystem + #define SQLITECPP_HAVE_STD_FILESYSTEM + #endif + #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + // macOS clang won't less us touch std::filesystem if we're targetting earlier than 10.15 + #else + #define SQLITECPP_HAVE_STD_FILESYSTEM + #endif +#elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L + #define SQLITECPP_HAVE_STD_FILESYSTEM +#endif + +#ifdef SQLITECPP_HAVE_STD_FILESYSTEM +#include <filesystem> +#endif // c++17 and a suitable compiler + +#include <memory> +#include <string.h> + +// Forward declarations to avoid inclusion of <sqlite3.h> in a header +struct sqlite3; +struct sqlite3_context; + +#ifndef SQLITE_USE_LEGACY_STRUCT // Since SQLITE 3.19 (used by default since SQLiteCpp 2.1.0) +typedef struct sqlite3_value sqlite3_value; +#else // Before SQLite 3.19 (legacy struct forward declaration can be activated with CMake SQLITECPP_LEGACY_STRUCT var) +struct Mem; +typedef struct Mem sqlite3_value; +#endif + + +namespace SQLite +{ + +// Those public constants enable most usages of SQLiteCpp without including <sqlite3.h> in the client application. + +/// The database is opened in read-only mode. If the database does not already exist, an error is returned. +extern const int OPEN_READONLY; // SQLITE_OPEN_READONLY +/// The database is opened for reading and writing if possible, or reading only if the file is write protected +/// by the operating system. In either case the database must already exist, otherwise an error is returned. +extern const int OPEN_READWRITE; // SQLITE_OPEN_READWRITE +/// With OPEN_READWRITE: The database is opened for reading and writing, and is created if it does not already exist. +extern const int OPEN_CREATE; // SQLITE_OPEN_CREATE +/// Enable URI filename interpretation, parsed according to RFC 3986 (ex. "file:data.db?mode=ro&cache=private") +extern const int OPEN_URI; // SQLITE_OPEN_URI +/// Open in memory database +extern const int OPEN_MEMORY; // SQLITE_OPEN_MEMORY +/// Open database in multi-thread threading mode +extern const int OPEN_NOMUTEX; // SQLITE_OPEN_NOMUTEX +/// Open database with thread-safety in serialized threading mode +extern const int OPEN_FULLMUTEX; // SQLITE_OPEN_FULLMUTEX +/// Open database with shared cache enabled +extern const int OPEN_SHAREDCACHE; // SQLITE_OPEN_SHAREDCACHE +/// Open database with shared cache disabled +extern const int OPEN_PRIVATECACHE; // SQLITE_OPEN_PRIVATECACHE +/// Database filename is not allowed to be a symbolic link (Note: only since SQlite 3.31.0 from 2020-01-22) +extern const int OPEN_NOFOLLOW; // SQLITE_OPEN_NOFOLLOW + + +extern const int OK; ///< SQLITE_OK (used by check() bellow) + +extern const char* VERSION; ///< SQLITE_VERSION string from the sqlite3.h used at compile time +extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from the sqlite3.h used at compile time + +/// Return SQLite version string using runtime call to the compiled library +const char* getLibVersion() noexcept; +/// Return SQLite version number using runtime call to the compiled library +int getLibVersionNumber() noexcept; + +// Public structure for representing all fields contained within the SQLite header. +// Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header +struct Header { + unsigned char headerStr[16]; + unsigned int pageSizeBytes; + unsigned char fileFormatWriteVersion; + unsigned char fileFormatReadVersion; + unsigned char reservedSpaceBytes; + unsigned char maxEmbeddedPayloadFrac; + unsigned char minEmbeddedPayloadFrac; + unsigned char leafPayloadFrac; + unsigned long fileChangeCounter; + unsigned long databaseSizePages; + unsigned long firstFreelistTrunkPage; + unsigned long totalFreelistPages; + unsigned long schemaCookie; + unsigned long schemaFormatNumber; + unsigned long defaultPageCacheSizeBytes; + unsigned long largestBTreePageNumber; + unsigned long databaseTextEncoding; + unsigned long userVersion; + unsigned long incrementalVaccumMode; + unsigned long applicationId; + unsigned long versionValidFor; + unsigned long sqliteVersion; +}; + +/** + * @brief RAII management of a SQLite Database Connection. + * + * A Database object manage a list of all SQLite Statements associated with the + * underlying SQLite 3 database connection. + * + * Resource Acquisition Is Initialization (RAII) means that the Database Connection + * is opened in the constructor and closed in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + * + * Thread-safety: a Database object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class Database +{ + friend class Statement; // Give Statement constructor access to the mSQLitePtr Connection Handle + +public: + /** + * @brief Open the provided database UTF-8 filename. + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const char* apFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const char* apVfs = nullptr); + + /** + * @brief Open the provided database UTF-8 filename. + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] aFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const std::string& aFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const std::string& aVfs = "") : + Database(aFilename.c_str(), aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) + { + } + + #ifdef SQLITECPP_HAVE_STD_FILESYSTEM + + /** + * @brief Open the provided database std::filesystem::path. + * + * @note This feature requires std=C++17 + * + * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior + * of the old sqlite3_open() function (READWRITE+CREATE). + * This makes sense if you want to use it on a readonly filesystem + * or to prevent creation of a void file when a required file is missing. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + * + * @param[in] apFilename Path/uri to the database file ("filename" sqlite3 parameter) + * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) + * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default + * + * @throw SQLite::Exception in case of error + */ + Database(const std::filesystem::path& apFilename, + const int aFlags = SQLite::OPEN_READONLY, + const int aBusyTimeoutMs = 0, + const std::string& aVfs = "") : + Database(reinterpret_cast<const char*>(apFilename.u8string().c_str()), + aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) + { + } + + #endif // have std::filesystem + + // Database is non-copyable + Database(const Database&) = delete; + Database& operator=(const Database&) = delete; + + // Database is movable + Database(Database&& aDatabase) = default; + Database& operator=(Database&& aDatabase) = default; + + /** + * @brief Close the SQLite database connection. + * + * All SQLite statements must have been finalized before, + * so all Statement objects must have been unregistered. + * + * @warning assert in case of error + */ + ~Database() = default; + + // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. + struct Deleter + { + void operator()(sqlite3* apSQLite); + }; + + /** + * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. + * + * This is useful in multithreaded program to handle case where a table is locked for writing by a thread. + * Any other thread cannot access the table and will receive a SQLITE_BUSY error: + * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. + * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". + * Default busy timeout is 0ms. + * + * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY + * + * @throw SQLite::Exception in case of error + */ + void setBusyTimeout(const int aBusyTimeoutMs); + + /** + * @brief Shortcut to execute one or multiple statements without results. Return the number of changes. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see Database::tryExec() to execute, returning the sqlite result code + * @see Statement::exec() to handle precompiled statements (for better performances) without results + * @see Statement::executeStep() to handle "SELECT" queries with results + * + * @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) + * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. + * + * @throw SQLite::Exception in case of error + */ + int exec(const char* apQueries); + + /** + * @brief Shortcut to execute one or multiple statements without results. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see Database::tryExec() to execute, returning the sqlite result code + * @see Statement::exec() to handle precompiled statements (for better performances) without results + * @see Statement::executeStep() to handle "SELECT" queries with results + * + * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) + * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. + * + * @throw SQLite::Exception in case of error + */ + int exec(const std::string& aQueries) + { + return exec(aQueries.c_str()); + } + + /** + * @brief Try to execute one or multiple statements, returning the sqlite result code. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see exec() to execute, returning number of rows modified + * + * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return the sqlite result code. + */ + int tryExec(const char* apQueries) noexcept; + + /** + * @brief Try to execute one or multiple statements, returning the sqlite result code. + * + * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * @see exec() to execute, returning number of rows modified + * + * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements + * + * @return the sqlite result code. + */ + int tryExec(const std::string aQueries) noexcept + { + return tryExec(aQueries.c_str()); + } + + /** + * @brief Shortcut to execute a one step query and fetch the first column of the result. + * + * This is a shortcut to execute a simple statement with a single result. + * This should be used only for non reusable queries (else you should use a Statement with bind()). + * This should be used only for queries with expected results (else an exception is fired). + * + * @warning WARNING: Be very careful with this dangerous method: you have to + * make a COPY OF THE result, else it will be destroy before the next line + * (when the underlying temporary Statement and Column objects are destroyed) + * + * @see also Statement class for handling queries with multiple results + * + * @param[in] apQuery an UTF-8 encoded SQL query + * + * @return a temporary Column object with the first column of result. + * + * @throw SQLite::Exception in case of error + */ + Column execAndGet(const char* apQuery); + + /** + * @brief Shortcut to execute a one step query and fetch the first column of the result. + * + * This is a shortcut to execute a simple statement with a single result. + * This should be used only for non reusable queries (else you should use a Statement with bind()). + * This should be used only for queries with expected results (else an exception is fired). + * + * @warning WARNING: Be very careful with this dangerous method: you have to + * make a COPY OF THE result, else it will be destroy before the next line + * (when the underlying temporary Statement and Column objects are destroyed) + * + * @see also Statement class for handling queries with multiple results + * + * @param[in] aQuery an UTF-8 encoded SQL query + * + * @return a temporary Column object with the first column of result. + * + * @throw SQLite::Exception in case of error + */ + Column execAndGet(const std::string& aQuery) + { + return execAndGet(aQuery.c_str()); + } + + /** + * @brief Shortcut to test if a table exists. + * + * Table names are case sensitive. + * + * @param[in] apTableName an UTF-8 encoded case sensitive Table name + * + * @return true if the table exists. + * + * @throw SQLite::Exception in case of error + */ + bool tableExists(const char* apTableName); + + /** + * @brief Shortcut to test if a table exists. + * + * Table names are case sensitive. + * + * @param[in] aTableName an UTF-8 encoded case sensitive Table name + * + * @return true if the table exists. + * + * @throw SQLite::Exception in case of error + */ + bool tableExists(const std::string& aTableName) + { + return tableExists(aTableName.c_str()); + } + + /** + * @brief Get the rowid of the most recent successful INSERT into the database from the current connection. + * + * Each entry in an SQLite table always has a unique 64-bit signed integer key called the rowid. + * If the table has a column of type INTEGER PRIMARY KEY, then it is an alias for the rowid. + * + * @return Rowid of the most recent successful INSERT into the database, or 0 if there was none. + */ + long long getLastInsertRowid() const noexcept; + + /// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). + int getChanges() const noexcept; + + /// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection (not DROP table). + int getTotalChanges() const noexcept; + + /// Return the numeric result code for the most recent failed API call (if any). + int getErrorCode() const noexcept; + /// Return the extended numeric result code for the most recent failed API call (if any). + int getExtendedErrorCode() const noexcept; + /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). + const char* getErrorMsg() const noexcept; + + /// Return the filename used to open the database. + const std::string& getFilename() const noexcept + { + return mFilename; + } + + /** + * @brief Return raw pointer to SQLite Database Connection Handle. + * + * This is often needed to mix this wrapper with other libraries or for advance usage not supported by SQLiteCpp. + */ + sqlite3* getHandle() const noexcept + { + return mSQLitePtr.get(); + } + + /** + * @brief Create or redefine a SQL function or aggregate in the sqlite database. + * + * This is the equivalent of the sqlite3_create_function_v2 command. + * @see http://www.sqlite.org/c3ref/create_function.html + * + * @note UTF-8 text encoding assumed. + * + * @param[in] apFuncName Name of the SQL function to be created or redefined + * @param[in] aNbArg Number of arguments in the function + * @param[in] abDeterministic Optimize for deterministic functions (most are). A random number generator is not. + * @param[in] apApp Arbitrary pointer of user data, accessible with sqlite3_user_data(). + * @param[in] apFunc Pointer to a C-function to implement a scalar SQL function (apStep & apFinal nullptr) + * @param[in] apStep Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) + * @param[in] apFinal Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) + * @param[in] apDestroy If not nullptr, then it is the destructor for the application data pointer. + * + * @throw SQLite::Exception in case of error + */ + void createFunction(const char* apFuncName, + int aNbArg, + bool abDeterministic, + void* apApp, + void (*apFunc)(sqlite3_context *, int, sqlite3_value **), + void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, + void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting) + void (*apDestroy)(void *) = nullptr); + + /** + * @brief Load a module into the current sqlite database instance. + * + * This is the equivalent of the sqlite3_load_extension call, but additionally enables + * module loading support prior to loading the requested module. + * + * @see http://www.sqlite.org/c3ref/load_extension.html + * + * @note UTF-8 text encoding assumed. + * + * @param[in] apExtensionName Name of the shared library containing extension + * @param[in] apEntryPointName Name of the entry point (nullptr to let sqlite work it out) + * + * @throw SQLite::Exception in case of error + */ + void loadExtension(const char* apExtensionName, const char* apEntryPointName); + + /** + * @brief Set the key for the current sqlite database instance. + * + * This is the equivalent of the sqlite3_key call and should thus be called + * directly after opening the database. + * Open encrypted database -> call db.key("secret") -> database ready + * + * @param[in] aKey Key to decode/encode the database + * + * @throw SQLite::Exception in case of error + */ + void key(const std::string& aKey) const; + + /** + * @brief Reset the key for the current sqlite database instance. + * + * This is the equivalent of the sqlite3_rekey call and should thus be called + * after the database has been opened with a valid key. To decrypt a + * database, call this method with an empty string. + * Open normal database -> call db.rekey("secret") -> encrypted database, database ready + * Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready + * Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready + * + * @param[in] aNewKey New key to encode the database + * + * @throw SQLite::Exception in case of error + */ + void rekey(const std::string& aNewKey) const; + + /** + * @brief Test if a file contains an unencrypted database. + * + * This is a simple test that reads the first bytes of a database file and + * compares them to the standard header for unencrypted databases. If the + * header does not match the standard string, we assume that we have an + * encrypted file. + * + * @param[in] aFilename path/uri to a file + * + * @return true if the database has the standard header. + * + * @throw SQLite::Exception in case of error + */ + static bool isUnencrypted(const std::string& aFilename); + + /** + * @brief Parse SQLite header data from a database file. + * + * This function reads the first 100 bytes of a SQLite database file + * and reconstructs groups of individual bytes into the associated fields + * in a Header object. + * + * @param[in] aFilename path/uri to a file + * + * @return Header object containing file data + * + * @throw SQLite::Exception in case of error + */ + static Header getHeaderInfo(const std::string& aFilename); + + // Parse SQLite header data from a database file. + Header getHeaderInfo() + { + return getHeaderInfo(mFilename); + } + + /** + * @brief BackupType for the backup() method + */ + enum BackupType { Save, Load }; + + /** + * @brief Load or save the database content. + * + * This function is used to load the contents of a database file on disk + * into the "main" database of open database connection, or to save the current + * contents of the database into a database file on disk. + * + * @throw SQLite::Exception in case of error + */ + void backup(const char* apFilename, BackupType aType); + + /** + * @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message + */ + void check(const int aRet) const + { + if (SQLite::OK != aRet) + { + throw SQLite::Exception(getHandle(), aRet); + } + } + +private: + // TODO: perhaps switch to having Statement sharing a pointer to the Connexion + std::unique_ptr<sqlite3, Deleter> mSQLitePtr; ///< Pointer to SQLite Database Connection Handle + std::string mFilename; ///< UTF-8 filename used to open the database +}; + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.cpp new file mode 100644 index 0000000..0dc644f --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.cpp @@ -0,0 +1,47 @@ +/** + * @file Exception.cpp + * @ingroup SQLiteCpp + * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Exception.h> + +#include <sqlite3.h> + + +namespace SQLite +{ + +Exception::Exception(const char* aErrorMessage, int ret) : + std::runtime_error(aErrorMessage), + mErrcode(ret), + mExtendedErrcode(-1) +{ +} + +Exception::Exception(sqlite3* apSQLite) : + std::runtime_error(sqlite3_errmsg(apSQLite)), + mErrcode(sqlite3_errcode(apSQLite)), + mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) +{ +} + +Exception::Exception(sqlite3* apSQLite, int ret) : + std::runtime_error(sqlite3_errmsg(apSQLite)), + mErrcode(ret), + mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) +{ +} + +// Return a string, solely based on the error code +const char* Exception::getErrorStr() const noexcept +{ + return sqlite3_errstr(mErrcode); +} + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.h new file mode 100644 index 0000000..efd6356 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Exception.h @@ -0,0 +1,92 @@ +/** + * @file Exception.h + * @ingroup SQLiteCpp + * @brief Encapsulation of the error message from SQLite3 on a std::runtime_error. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <stdexcept> +#include <string> + +// Forward declaration to avoid inclusion of <sqlite3.h> in a header +struct sqlite3; + +namespace SQLite +{ + + +/** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + */ +class Exception : public std::runtime_error +{ +public: + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] aErrorMessage The string message describing the SQLite error + * @param[in] ret Return value from function call that failed. + */ + Exception(const char* aErrorMessage, int ret); + + Exception(const std::string& aErrorMessage, int ret) : + Exception(aErrorMessage.c_str(), ret) + { + } + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] aErrorMessage The string message describing the SQLite error + */ + explicit Exception(const char* aErrorMessage) : + Exception(aErrorMessage, -1) // 0 would be SQLITE_OK, which doesn't make sense + { + } + explicit Exception(const std::string& aErrorMessage) : + Exception(aErrorMessage.c_str(), -1) // 0 would be SQLITE_OK, which doesn't make sense + { + } + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + */ + explicit Exception(sqlite3* apSQLite); + + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + * @param[in] ret Return value from function call that failed. + */ + Exception(sqlite3* apSQLite, int ret); + + /// Return the result code (if any, otherwise -1). + int getErrorCode() const noexcept + { + return mErrcode; + } + + /// Return the extended numeric result code (if any, otherwise -1). + int getExtendedErrorCode() const noexcept + { + return mExtendedErrcode; + } + + /// Return a string, solely based on the error code + const char* getErrorStr() const noexcept; + +private: + int mErrcode; ///< Error code value + int mExtendedErrcode; ///< Detailed error code if any +}; + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/ExecuteMany.h b/3rdparty/sqlitecpp/source/SQLiteCpp/ExecuteMany.h new file mode 100644 index 0000000..4e7dac9 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/ExecuteMany.h @@ -0,0 +1,90 @@ +/** + * @file ExecuteMany.h + * @ingroup SQLiteCpp + * @brief Convenience function to execute a Statement with multiple Parameter sets + * + * Copyright (c) 2019 Maximilian Bachmann ([email protected]) + * Copyright (c) 2019-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 + +#include <SQLiteCpp/Statement.h> +#include <SQLiteCpp/VariadicBind.h> + +/// @cond +#include <tuple> +#include <utility> +#include <initializer_list> + +namespace SQLite +{ +/// @endcond + +/** + * \brief Convenience function to execute a Statement with multiple Parameter sets once for each parameter set given. + * + * + * This feature requires a c++14 capable compiler. + * + * \code{.cpp} + * execute_many(db, "INSERT INTO test VALUES (?, ?)", + * 1, + * std::make_tuple(2), + * std::make_tuple(3, "three") + * ); + * \endcode + * @param aDatabase Database to use + * @param apQuery Query to use with all parameter sets + * @param aArg first tuple with parameters + * @param aParams the following tuples with parameters + */ +template <typename Arg, typename... Types> +void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams) +{ + SQLite::Statement query(aDatabase, apQuery); + bind_exec(query, std::forward<Arg>(aArg)); + (void)std::initializer_list<int> + { + ((void)reset_bind_exec(query, std::forward<Types>(aParams)), 0)... + }; +} + +/** + * \brief Convenience function to reset a statement and call bind_exec to + * bind new values to the statement and execute it + * + * This feature requires a c++14 capable compiler. + * + * @param apQuery Query to use + * @param aTuple Tuple to bind + */ +template <typename TupleT> +void reset_bind_exec(Statement& apQuery, TupleT&& aTuple) +{ + apQuery.reset(); + bind_exec(apQuery, std::forward<TupleT>(aTuple)); +} + +/** + * \brief Convenience function to bind values a the statement and execute it + * + * This feature requires a c++14 capable compiler. + * + * @param apQuery Query to use + * @param aTuple Tuple to bind + */ +template <typename TupleT> +void bind_exec(Statement& apQuery, TupleT&& aTuple) +{ + SQLite::bind(apQuery, std::forward<TupleT>(aTuple)); + while (apQuery.executeStep()) {} +} + +} // namespace SQLite + +#endif // c++14 diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/SQLiteCpp.h b/3rdparty/sqlitecpp/source/SQLiteCpp/SQLiteCpp.h new file mode 100644 index 0000000..161478c --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/SQLiteCpp.h @@ -0,0 +1,44 @@ +/** + * @file SQLiteCpp.h + * @ingroup SQLiteCpp + * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. + * + * Include this main header file in your project to gain access to all functionality provided by the wrapper. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +/** + * @defgroup SQLiteCpp SQLiteC++ + * @brief SQLiteC++ is a smart and simple C++ SQLite3 wrapper. This file is only "easy include" for other files. + */ +#pragma once + + +// Include useful headers of SQLiteC++ +#include <SQLiteCpp/Assertion.h> +#include <SQLiteCpp/Exception.h> +#include <SQLiteCpp/Database.h> +#include <SQLiteCpp/Statement.h> +#include <SQLiteCpp/Column.h> +#include <SQLiteCpp/Transaction.h> + + +/** + * @brief Version numbers for SQLiteC++ are provided in the same way as sqlite3.h + * + * The [SQLITECPP_VERSION] C preprocessor macro in the SQLiteC++.h header + * evaluates to a string literal that is the SQLite version in the + * format "X.Y.Z" where X is the major version number + * and Y is the minor version number and Z is the release number. + * + * The [SQLITECPP_VERSION_NUMBER] C preprocessor macro resolves to an integer + * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same + * numbers used in [SQLITECPP_VERSION]. + * + * WARNING: shall always be updated in sync with PROJECT_VERSION in CMakeLists.txt + */ +#define SQLITECPP_VERSION "3.01.01" // 3.1.1 +#define SQLITECPP_VERSION_NUMBER 3001001 // 3.1.1 diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.cpp new file mode 100644 index 0000000..b3d13a2 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.cpp @@ -0,0 +1,65 @@ +/** + * @file Savepoint.cpp + * @ingroup SQLiteCpp + * @brief A Savepoint is a way to group multiple SQL statements into an atomic + * secured operation. Similar to a transaction while allowing child savepoints. + * + * Copyright (c) 2020 Kelvin Hammond ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or + * copy at http://opensource.org/licenses/MIT) + */ + +#include <SQLiteCpp/Assertion.h> +#include <SQLiteCpp/Database.h> +#include <SQLiteCpp/Savepoint.h> +#include <SQLiteCpp/Statement.h> + +namespace SQLite { + +// Begins the SQLite savepoint +Savepoint::Savepoint(Database& aDatabase, std::string aName) + : mDatabase(aDatabase), msName(aName), mbReleased(false) { + // workaround because you cannot bind to SAVEPOINT + // escape name for use in query + Statement stmt(mDatabase, "SELECT quote(?)"); + stmt.bind(1, msName); + stmt.executeStep(); + msName = stmt.getColumn(0).getText(); + + mDatabase.exec(std::string("SAVEPOINT ") + msName); +} + +// Safely rollback the savepoint if it has not been committed. +Savepoint::~Savepoint() { + if (!mbReleased) { + try { + rollback(); + } catch (SQLite::Exception&) { + // Never throw an exception in a destructor: error if already rolled + // back or released, but no harm is caused by this. + } + } +} + +// Release the savepoint and commit +void Savepoint::release() { + if (!mbReleased) { + mDatabase.exec(std::string("RELEASE SAVEPOINT ") + msName); + mbReleased = true; + } else { + throw SQLite::Exception("Savepoint already released or rolled back."); + } +} + +// Rollback the savepoint +void Savepoint::rollback() { + if (!mbReleased) { + mDatabase.exec(std::string("ROLLBACK TO SAVEPOINT ") + msName); + mbReleased = true; + } else { + throw SQLite::Exception("Savepoint already released or rolled back."); + } +} + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.h new file mode 100644 index 0000000..d389720 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Savepoint.h @@ -0,0 +1,95 @@ +/** + * @file Savepoint.h + * @ingroup SQLiteCpp + * @brief A Savepoint is a way to group multiple SQL statements into an atomic + * secured operation. Similar to a transaction while allowing child savepoints. + * + * Copyright (c) 2020 Kelvin Hammond ([email protected]) + * Copyright (c) 2020-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt or + * copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Exception.h> + +namespace SQLite { + +// Foward declaration +class Database; + +/** + * @brief RAII encapsulation of a SQLite Savepoint. + * + * A Savepoint is a way to group multiple SQL statements into an atomic + * secureced operation; either it succeeds, with all the changes commited to the + * database file, or if it fails, all the changes are rolled back to the initial + * state at the start of the savepoint. + * + * This method also offers big performances improvements compared to + * individually executed statements. + * + * Caveats: + * + * 1) Calling COMMIT or commiting a parent transaction or RELEASE on a parent + * savepoint will cause this savepoint to be released. + * + * 2) Calling ROLLBACK or rolling back a parent savepoint will cause this + * savepoint to be rolled back. + * + * 3) This savepoint is not saved to the database until this and all savepoints + * or transaction in the savepoint stack have been released or commited. + * + * See also: https://sqlite.org/lang_savepoint.html + * + * Thread-safety: a Transaction object shall not be shared by multiple threads, + * because: + * + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple + * threads provided that no single database connection is used simultaneously in + * two or more threads." + * + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, because of the + * way it shares the underling SQLite precompiled statement in a custom shared + * pointer (See the inner class "Statement::Ptr"). + */ + +class Savepoint { + public: + /** + * @brief Begins the SQLite savepoint + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] aName the name of the Savepoint + * + * Exception is thrown in case of error, then the Savepoint is NOT + * initiated. + */ + Savepoint(Database& aDatabase, std::string name); + + // Savepoint is non-copyable + Savepoint(const Savepoint&) = delete; + Savepoint& operator=(const Savepoint&) = delete; + + /** + * @brief Safely rollback the savepoint if it has not been commited. + */ + ~Savepoint(); + + /** + * @brief Commit and release the savepoint. + */ + void release(); + + /** + * @brief Rollback the savepoint + */ + void rollback(); + + private: + Database& mDatabase; ///< Reference to the SQLite Database Connection + std::string msName; ///< Name of the Savepoint + bool mbReleased; ///< True when release has been called +}; +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.cpp new file mode 100644 index 0000000..64e16d2 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.cpp @@ -0,0 +1,368 @@ +/** + * @file Statement.cpp + * @ingroup SQLiteCpp + * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Statement.h> + +#include <SQLiteCpp/Database.h> +#include <SQLiteCpp/Column.h> +#include <SQLiteCpp/Assertion.h> +#include <SQLiteCpp/Exception.h> + +#include <sqlite3.h> + +namespace SQLite +{ + +Statement::Statement(const Database& aDatabase, const char* apQuery) : + mQuery(apQuery), + mpSQLite(aDatabase.getHandle()), + mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship) +{ + mColumnCount = sqlite3_column_count(mpPreparedStatement.get()); +} + +Statement::Statement(Statement&& aStatement) noexcept : + mQuery(std::move(aStatement.mQuery)), + mpSQLite(aStatement.mpSQLite), + mpPreparedStatement(std::move(aStatement.mpPreparedStatement)), + mColumnCount(aStatement.mColumnCount), + mbHasRow(aStatement.mbHasRow), + mbDone(aStatement.mbDone), + mColumnNames(std::move(aStatement.mColumnNames)) +{ + aStatement.mpSQLite = nullptr; + aStatement.mColumnCount = 0; + aStatement.mbHasRow = false; + aStatement.mbDone = false; +} + +// Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) +void Statement::reset() +{ + const int ret = tryReset(); + check(ret); +} + +int Statement::tryReset() noexcept +{ + mbHasRow = false; + mbDone = false; + return sqlite3_reset(mpPreparedStatement.get()); +} + +// Clears away all the bindings of a prepared statement (can be associated with #reset() above). +void Statement::clearBindings() +{ + const int ret = sqlite3_clear_bindings(getPreparedStatement()); + check(ret); +} + +int Statement::getIndex(const char * const apName) const +{ + return sqlite3_bind_parameter_index(getPreparedStatement(), apName); +} + +// Bind an 32bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const int32_t aValue) +{ + const int ret = sqlite3_bind_int(getPreparedStatement(), aIndex, aValue); + check(ret); +} + +// Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const uint32_t aValue) +{ + const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue); + check(ret); +} + +// Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const int64_t aValue) +{ + const int ret = sqlite3_bind_int64(getPreparedStatement(), aIndex, aValue); + check(ret); +} + +// Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const double aValue) +{ + const int ret = sqlite3_bind_double(getPreparedStatement(), aIndex, aValue); + check(ret); +} + +// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const std::string& aValue) +{ + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), + static_cast<int>(aValue.size()), SQLITE_TRANSIENT); + check(ret); +} + +// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const char* apValue) +{ + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_TRANSIENT); + check(ret); +} + +// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex, const void* apValue, const int aSize) +{ + const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_TRANSIENT); + check(ret); +} + +// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bindNoCopy(const int aIndex, const std::string& aValue) +{ + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), + static_cast<int>(aValue.size()), SQLITE_STATIC); + check(ret); +} + +// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bindNoCopy(const int aIndex, const char* apValue) +{ + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, apValue, -1, SQLITE_STATIC); + check(ret); +} + +// Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize) +{ + const int ret = sqlite3_bind_blob(getPreparedStatement(), aIndex, apValue, aSize, SQLITE_STATIC); + check(ret); +} + +// Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bind(const int aIndex) +{ + const int ret = sqlite3_bind_null(getPreparedStatement(), aIndex); + check(ret); +} + + +// Execute a step of the query to fetch one row of results +bool Statement::executeStep() +{ + const int ret = tryExecuteStep(); + if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem + { + if (ret == sqlite3_errcode(mpSQLite)) + { + throw SQLite::Exception(mpSQLite, ret); + } + else + { + throw SQLite::Exception("Statement needs to be reseted", ret); + } + } + + return mbHasRow; // true only if one row is accessible by getColumn(N) +} + +// Execute a one-step query with no expected result, and return the number of changes. +int Statement::exec() +{ + const int ret = tryExecuteStep(); + if (SQLITE_DONE != ret) // the statement has finished executing successfully + { + if (SQLITE_ROW == ret) + { + throw SQLite::Exception("exec() does not expect results. Use executeStep."); + } + else if (ret == sqlite3_errcode(mpSQLite)) + { + throw SQLite::Exception(mpSQLite, ret); + } + else + { + throw SQLite::Exception("Statement needs to be reseted", ret); + } + } + + // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) + return sqlite3_changes(mpSQLite); +} + +int Statement::tryExecuteStep() noexcept +{ + if (mbDone) + { + return SQLITE_MISUSE; // Statement needs to be reseted ! + } + + const int ret = sqlite3_step(mpPreparedStatement.get()); + if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it + { + mbHasRow = true; + } + else + { + mbHasRow = false; + mbDone = SQLITE_DONE == ret; // check if the query has finished executing + } + return ret; +} + + +// Return a copy of the column data specified by its index starting at 0 +// (use the Column copy-constructor) +Column Statement::getColumn(const int aIndex) const +{ + checkRow(); + checkIndex(aIndex); + + // Share the Statement Object handle with the new Column created + return Column(mpPreparedStatement, aIndex); +} + +// Return a copy of the column data specified by its column name starting at 0 +// (use the Column copy-constructor) +Column Statement::getColumn(const char* apName) const +{ + checkRow(); + const int index = getColumnIndex(apName); + + // Share the Statement Object handle with the new Column created + return Column(mpPreparedStatement, index); +} + +// Test if the column is NULL +bool Statement::isColumnNull(const int aIndex) const +{ + checkRow(); + checkIndex(aIndex); + return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex)); +} + +bool Statement::isColumnNull(const char* apName) const +{ + checkRow(); + const int index = getColumnIndex(apName); + return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), index)); +} + +// Return the named assigned to the specified result column (potentially aliased) +const char* Statement::getColumnName(const int aIndex) const +{ + checkIndex(aIndex); + return sqlite3_column_name(getPreparedStatement(), aIndex); +} + +#ifdef SQLITE_ENABLE_COLUMN_METADATA +// Return the named assigned to the specified result column (potentially aliased) +const char* Statement::getColumnOriginName(const int aIndex) const +{ + checkIndex(aIndex); + return sqlite3_column_origin_name(getPreparedStatement(), aIndex); +} +#endif + +// Return the index of the specified (potentially aliased) column name +int Statement::getColumnIndex(const char* apName) const +{ + // Build the map of column index by name on first call + if (mColumnNames.empty()) + { + for (int i = 0; i < mColumnCount; ++i) + { + const char* pName = sqlite3_column_name(getPreparedStatement(), i); + mColumnNames[pName] = i; + } + } + + const auto iIndex = mColumnNames.find(apName); + if (iIndex == mColumnNames.end()) + { + throw SQLite::Exception("Unknown column name."); + } + + return iIndex->second; +} + +const char * Statement::getColumnDeclaredType(const int aIndex) const +{ + checkIndex(aIndex); + const char * result = sqlite3_column_decltype(getPreparedStatement(), aIndex); + if (!result) + { + throw SQLite::Exception("Could not determine declared column type."); + } + else + { + return result; + } +} + +// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). +int Statement::getChanges() const noexcept +{ + return sqlite3_changes(mpSQLite); +} + +int Statement::getBindParameterCount() const noexcept +{ + return sqlite3_bind_parameter_count(mpPreparedStatement.get()); +} + +// Return the numeric result code for the most recent failed API call (if any). +int Statement::getErrorCode() const noexcept +{ + return sqlite3_errcode(mpSQLite); +} + +// Return the extended numeric result code for the most recent failed API call (if any). +int Statement::getExtendedErrorCode() const noexcept +{ + return sqlite3_extended_errcode(mpSQLite); +} + +// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). +const char* Statement::getErrorMsg() const noexcept +{ + return sqlite3_errmsg(mpSQLite); +} + +// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. +std::string Statement::getExpandedSQL() const { + char* expanded = sqlite3_expanded_sql(getPreparedStatement()); + std::string expandedString(expanded); + sqlite3_free(expanded); + return expandedString; +} + +// Prepare SQLite statement object and return shared pointer to this object +Statement::TStatementPtr Statement::prepareStatement() +{ + sqlite3_stmt* statement; + const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast<int>(mQuery.size()), &statement, nullptr); + if (SQLITE_OK != ret) + { + throw SQLite::Exception(mpSQLite, ret); + } + return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt) + { + sqlite3_finalize(stmt); + }); +} + +// Return prepered statement object or throw +sqlite3_stmt* Statement::getPreparedStatement() const +{ + sqlite3_stmt* ret = mpPreparedStatement.get(); + if (ret) + { + return ret; + } + throw SQLite::Exception("Statement was not prepared."); +} + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.h new file mode 100644 index 0000000..c81e215 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Statement.h @@ -0,0 +1,714 @@ +/** + * @file Statement.h + * @ingroup SQLiteCpp + * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Exception.h> +#include <SQLiteCpp/Utils.h> // SQLITECPP_PURE_FUNC + +#include <string> +#include <map> +#include <memory> + +// Forward declarations to avoid inclusion of <sqlite3.h> in a header +struct sqlite3; +struct sqlite3_stmt; + + +namespace SQLite +{ + + +// Forward declaration +class Database; +class Column; + +extern const int OK; ///< SQLITE_OK + +/** + * @brief RAII encapsulation of a prepared SQLite Statement. + * + * A Statement is a compiled SQL query ready to be executed step by step + * to provide results one row at a time. + * + * Resource Acquisition Is Initialization (RAII) means that the Statement + * is compiled in the constructor and finalized in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Statement. + * + * Thread-safety: a Statement object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class Statement +{ +public: + /** + * @brief Compile and register the SQL query for the provided SQLite Database Connection + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] apQuery an UTF-8 encoded query string + * + * Exception is thrown in case of error, then the Statement object is NOT constructed. + */ + Statement(const Database& aDatabase, const char* apQuery); + + /** + * @brief Compile and register the SQL query for the provided SQLite Database Connection + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] aQuery an UTF-8 encoded query string + * + * Exception is thrown in case of error, then the Statement object is NOT constructed. + */ + Statement(const Database& aDatabase, const std::string& aQuery) : + Statement(aDatabase, aQuery.c_str()) + {} + + /** + * @brief Move an SQLite statement. + * + * @param[in] aStatement Statement to move + */ + Statement(Statement&& aStatement) noexcept; + Statement& operator=(Statement&& aStatement) noexcept = default; + + // Statement is non-copyable + Statement(const Statement&) = delete; + Statement& operator=(const Statement&) = delete; + + /// Finalize and unregister the SQL query from the SQLite Database Connection. + /// The finalization will be done by the destructor of the last shared pointer + ~Statement() = default; + + /// Reset the statement to make it ready for a new execution. Throws an exception on error. + void reset(); + + /// Reset the statement. Returns the sqlite result code instead of throwing an exception on error. + int tryReset() noexcept; + + /** + * @brief Clears away all the bindings of a prepared statement. + * + * Contrary to the intuition of many, reset() does not reset the bindings on a prepared statement. + * Use this routine to reset all parameters to NULL. + */ + void clearBindings(); // throw(SQLite::Exception) + + //////////////////////////////////////////////////////////////////////////// + // Bind a value to a parameter of the SQL statement, + // in the form "?" (unnamed), "?NNN", ":VVV", "@VVV" or "$VVV". + // + // Can use the parameter index, starting from "1", to the higher NNN value, + // or the complete parameter name "?NNN", ":VVV", "@VVV" or "$VVV" + // (prefixed with the corresponding sign "?", ":", "@" or "$") + // + // Note that for text and blob values, the SQLITE_TRANSIENT flag is used, + // which tell the sqlite library to make its own copy of the data before the bind() call returns. + // This choice is done to prevent any common misuses, like passing a pointer to a + // dynamic allocated and temporary variable (a std::string for instance). + // This is under-optimized for static data (a static text define in code) + // as well as for dynamic allocated buffer which could be transfer to sqlite + // instead of being copied. + // => if you know what you are doing, use bindNoCopy() instead of bind() + + SQLITECPP_PURE_FUNC + int getIndex(const char * const apName) const; + + /** + * @brief Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const int32_t aValue); + /** + * @brief Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const uint32_t aValue); + /** + * @brief Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const int64_t aValue); + /** + * @brief Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const int aIndex, const double aValue); + /** + * @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const std::string& aValue); + /** + * @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const char* apValue); + /** + * @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const int aIndex, const void* apValue, const int aSize); + /** + * @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1). + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const std::string& aValue); + /** + * @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const char* apValue); + /** + * @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const int aIndex, const void* apValue, const int aSize); + /** + * @brief Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const int aIndex); + + /** + * @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const int32_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const uint32_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const int64_t aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const char* apName, const double aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const std::string& aValue) + { + bind(getIndex(apName), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const char* apValue) + { + bind(getIndex(apName), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const char* apName, const void* apValue, const int aSize) + { + bind(getIndex(apName), apValue, aSize); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const std::string& aValue) + { + bindNoCopy(getIndex(apName), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const char* apValue) + { + bindNoCopy(getIndex(apName), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const char* apName, const void* apValue, const int aSize) + { + bindNoCopy(getIndex(apName), apValue, aSize); + } + /** + * @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const char* apName) // bind NULL value + { + bind(getIndex(apName)); + } + + + /** + * @brief Bind an int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const int32_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a 32bits unsigned int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const uint32_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a 64bits int value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const int64_t aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a double (64bits float) value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + */ + void bind(const std::string& aName, const double aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const std::string& aValue) + { + bind(aName.c_str(), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const char* apValue) + { + bind(aName.c_str(), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use + */ + void bind(const std::string& aName, const void* apValue, const int aSize) + { + bind(aName.c_str(), apValue, aSize); + } + /** + * @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * The string can contain null characters as it is binded using its size. + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const std::string& aValue) + { + bindNoCopy(aName.c_str(), aValue); + } + /** + * @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * Main usage is with null-terminated literal text (aka in code static strings) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const char* apValue) + { + bindNoCopy(aName.c_str(), apValue); + } + /** + * @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement. + */ + void bindNoCopy(const std::string& aName, const void* apValue, const int aSize) + { + bindNoCopy(aName.c_str(), apValue, aSize); + } + /** + * @brief Bind a NULL value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1) + * + * @see clearBindings() to set all bound parameters to NULL. + */ + void bind(const std::string& aName) // bind NULL value + { + bind(aName.c_str()); + } + + //////////////////////////////////////////////////////////////////////////// + + /** + * @brief Execute a step of the prepared query to fetch one row of results. + * + * While true is returned, a row of results is available, and can be accessed + * through the getColumn() method + * + * @see exec() execute a one-step prepared statement with no expected result + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it + * then you have to call executeStep() again to fetch more rows until the query is finished + * - false (SQLITE_DONE) if the query has finished executing : there is no (more) row of result + * (case of a query with no result, or after N rows fetched successfully) + * + * @throw SQLite::Exception in case of error + */ + bool executeStep(); + + /** + * @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * + * + * + * @see exec() execute a one-step prepared statement with no expected result + * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return the sqlite result code. + */ + int tryExecuteStep() noexcept; + + /** + * @brief Execute a one-step query with no expected result, and return the number of changes. + * + * This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * It is similar to Database::exec(), but using a precompiled statement, it adds : + * - the ability to bind() arguments to it (best way to insert data), + * - reusing it allows for better performances (efficient for multiple insertion). + * + * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE) + * + * @throw SQLite::Exception in case of error, or if row of results are returned while they are not expected! + */ + int exec(); + + //////////////////////////////////////////////////////////////////////////// + + /** + * @brief Return a copy of the column data specified by its index + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from: + * - if provided index is out of bound + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + * + * @param[in] aIndex Index of the column, starting at 0 + * + * @note This method is not const, reflecting the fact that the returned Column object will + * share the ownership of the underlying sqlite3_stmt. + * + * @warning The resulting Column object must not be memorized "as-is". + * Is is only a wrapper around the current result row, so it is only valid + * while the row from the Statement remains valid, that is only until next executeStep() call. + * Thus, you should instead extract immediately its data (getInt(), getText()...) + * and use or copy this data for any later usage. + */ + Column getColumn(const int aIndex) const; + + /** + * @brief Return a copy of the column data specified by its column name (less efficient than using an index) + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from : + * - if provided name is not one of the aliased column names + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified name is not an on of the aliased name of the columns in the result. + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @note Uses a map of column names to indexes, build on first call. + * + * @note This method is not const, reflecting the fact that the returned Column object will + * share the ownership of the underlying sqlite3_stmt. + * + * @warning The resulting Column object must not be memorized "as-is". + * Is is only a wrapper around the current result row, so it is only valid + * while the row from the Statement remains valid, that is only until next executeStep() call. + * Thus, you should instead extract immediately its data (getInt(), getText()...) + * and use or copy this data for any later usage. + * + * Throw an exception if the specified name is not one of the aliased name of the columns in the result. + */ + Column getColumn(const char* apName) const; + +#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) // c++14: Visual Studio 2015 + /** + * @brief Return an instance of T constructed from copies of the first N columns + * + * Can be used to access the data of the current row of result when applicable, + * while the executeStep() method returns true. + * + * Throw an exception if there is no row to return a Column from: + * - if provided column count is out of bound + * - before any executeStep() call + * - after the last executeStep() returned false + * - after a reset() call + * + * Throw an exception if the specified column count is out of the [0, getColumnCount()) range. + * + * @tparam T Object type to construct + * @tparam N Number of columns + * + * @note Requires std=C++14 + */ + template<typename T, int N> + T getColumns(); + +private: + /** + * @brief Helper function used by getColumns<typename T, int N> to expand an integer_sequence used to generate + * the required Column objects + */ + template<typename T, const int... Is> + T getColumns(const std::integer_sequence<int, Is...>); + +public: +#endif + + /** + * @brief Test if the column value is NULL + * + * @param[in] aIndex Index of the column, starting at 0 + * + * @return true if the column value is NULL + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + bool isColumnNull(const int aIndex) const; + + /** + * @brief Test if the column value is NULL + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @return true if the column value is NULL + * + * Throw an exception if the specified name is not one of the aliased name of the columns in the result. + */ + bool isColumnNull(const char* apName) const; + + /** + * @brief Return a pointer to the named assigned to the specified result column (potentially aliased) + * + * @param[in] aIndex Index of the column in the range [0, getColumnCount()). + * + * @see getColumnOriginName() to get original column name (not aliased) + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + const char* getColumnName(const int aIndex) const; + +#ifdef SQLITE_ENABLE_COLUMN_METADATA + /** + * @brief Return a pointer to the table column name that is the origin of the specified result column + * + * Require definition of the SQLITE_ENABLE_COLUMN_METADATA preprocessor macro : + * - when building the SQLite library itself (which is the case for the Debian libsqlite3 binary for instance), + * - and also when compiling this wrapper. + * + * Throw an exception if the specified index is out of the [0, getColumnCount()) range. + */ + const char* getColumnOriginName(const int aIndex) const; +#endif + + /** + * @brief Return the index of the specified (potentially aliased) column name + * + * @param[in] apName Aliased name of the column, that is, the named specified in the query (not the original name) + * + * @note Uses a map of column names to indexes, build on first call. + * + * Throw an exception if the specified name is not known. + */ + int getColumnIndex(const char* apName) const; + + + /** + * @brief Return the declared type of the specified result column for a SELECT statement. + * + * This is the type given at creation of the column and not the actual data type. + * SQLite stores data types dynamically for each value and not per column. + * + * @param[in] aIndex Index of the column in the range [0, getColumnCount()). + * + * Throw an exception if the type can't be determined because: + * - the specified index is out of the [0, getColumnCount()) range + * - the statement is not a SELECT query + * - the column at aIndex is not a table column but an expression or subquery + */ + const char * getColumnDeclaredType(const int aIndex) const; + + + /// Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table). + int getChanges() const noexcept; + + + //////////////////////////////////////////////////////////////////////////// + + /// Return the UTF-8 SQL Query. + const std::string& getQuery() const + { + return mQuery; + } + + // Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. + std::string getExpandedSQL() const; + + /// Return the number of columns in the result set returned by the prepared statement + int getColumnCount() const + { + return mColumnCount; + } + /// true when a row has been fetched with executeStep() + bool hasRow() const + { + return mbHasRow; + } + /// true when the last executeStep() had no more row to fetch + bool isDone() const + { + return mbDone; + } + + /// Return the number of bind parameters in the statement + int getBindParameterCount() const noexcept; + + /// Return the numeric result code for the most recent failed API call (if any). + int getErrorCode() const noexcept; + /// Return the extended numeric result code for the most recent failed API call (if any). + int getExtendedErrorCode() const noexcept; + /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). + const char* getErrorMsg() const noexcept; + + /// Shared pointer to SQLite Prepared Statement Object + using TStatementPtr = std::shared_ptr<sqlite3_stmt>; + +private: + /** + * @brief Check if a return code equals SQLITE_OK, else throw a SQLite::Exception with the SQLite error message + * + * @param[in] aRet SQLite return code to test against the SQLITE_OK expected value + */ + void check(const int aRet) const + { + if (SQLite::OK != aRet) + { + throw SQLite::Exception(mpSQLite, aRet); + } + } + + /** + * @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception. + */ + void checkRow() const + { + if (false == mbHasRow) + { + throw SQLite::Exception("No row to get a column from. executeStep() was not called, or returned false."); + } + } + + /** + * @brief Check if there is a Column index is in the range of columns in the result. + */ + void checkIndex(const int aIndex) const + { + if ((aIndex < 0) || (aIndex >= mColumnCount)) + { + throw SQLite::Exception("Column index out of range."); + } + } + + /** + * @brief Prepare statement object. + * + * @return Shared pointer to prepared statement object + */ + TStatementPtr prepareStatement(); + + /** + * @brief Return a prepared statement object. + * + * Throw an exception if the statement object was not prepared. + * @return raw pointer to Prepared Statement Object + */ + sqlite3_stmt* getPreparedStatement() const; + + std::string mQuery; //!< UTF-8 SQL Query + sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle + TStatementPtr mpPreparedStatement; //!< Shared Pointer to the prepared SQLite Statement Object + int mColumnCount{0}; //!< Number of columns in the result of the prepared statement + bool mbHasRow{false}; //!< true when a row has been fetched with executeStep() + bool mbDone{false}; //!< true when the last executeStep() had no more row to fetch + + /// Map of columns index by name (mutable so getColumnIndex can be const) + mutable std::map<std::string, int> mColumnNames{}; +}; + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.cpp b/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.cpp new file mode 100644 index 0000000..2a8857a --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.cpp @@ -0,0 +1,83 @@ +/** + * @file Transaction.cpp + * @ingroup SQLiteCpp + * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. + * + * Copyright (c) 2012-2019 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include <SQLiteCpp/Transaction.h> + +#include <SQLiteCpp/Database.h> +#include <SQLiteCpp/Assertion.h> + +#include <sqlite3.h> + +namespace SQLite +{ + + +// Begins the SQLite transaction +Transaction::Transaction(Database& aDatabase, TransactionBehavior behavior) : + mDatabase(aDatabase), + mbCommited(false) +{ + const char *stmt; + switch (behavior) { + case TransactionBehavior::DEFERRED: + stmt = "BEGIN DEFERRED"; + break; + case TransactionBehavior::IMMEDIATE: + stmt = "BEGIN IMMEDIATE"; + break; + case TransactionBehavior::EXCLUSIVE: + stmt = "BEGIN EXCLUSIVE"; + break; + default: + throw SQLite::Exception("invalid/unknown transaction behavior", SQLITE_ERROR); + } + mDatabase.exec(stmt); +} + +// Begins the SQLite transaction +Transaction::Transaction(Database &aDatabase) : + mDatabase(aDatabase), + mbCommited(false) +{ + mDatabase.exec("BEGIN"); +} + +// Safely rollback the transaction if it has not been committed. +Transaction::~Transaction() +{ + if (false == mbCommited) + { + try + { + mDatabase.exec("ROLLBACK"); + } + catch (SQLite::Exception&) + { + // Never throw an exception in a destructor: error if already rollbacked, but no harm is caused by this. + } + } +} + +// Commit the transaction. +void Transaction::commit() +{ + if (false == mbCommited) + { + mDatabase.exec("COMMIT"); + mbCommited = true; + } + else + { + throw SQLite::Exception("Transaction already committed."); + } +} + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.h new file mode 100644 index 0000000..d7f26ca --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Transaction.h @@ -0,0 +1,95 @@ +/** + * @file Transaction.h + * @ingroup SQLiteCpp + * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. + * + * Copyright (c) 2012-2021 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Exception.h> + + +namespace SQLite +{ + + +// Forward declaration +class Database; + +/** + * @brief Transaction behaviors when opening an SQLite transaction. + * Names correspond directly to the behavior. + */ +enum class TransactionBehavior { + DEFERRED, + IMMEDIATE, + EXCLUSIVE, +}; + +/** + * @brief RAII encapsulation of a SQLite Transaction. + * + * A Transaction is a way to group multiple SQL statements into an atomic secured operation; + * either it succeeds, with all the changes committed to the database file, + * or if it fails, all the changes are rolled back to the initial state. + * + * Resource Acquisition Is Initialization (RAII) means that the Transaction + * begins in the constructor and is rollbacked in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + * + * This method also offers big performances improvements compared to individually executed statements. + * + * Thread-safety: a Transaction object shall not be shared by multiple threads, because : + * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads + * provided that no single database connection is used simultaneously in two or more threads." + * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, + * because of the way it shares the underling SQLite precompiled statement + * in a custom shared pointer (See the inner class "Statement::Ptr"). + */ +class Transaction +{ +public: + /** + * @brief Begins the SQLite transaction using the default transaction behavior. + * + * @param[in] aDatabase the SQLite Database Connection + * + * Exception is thrown in case of error, then the Transaction is NOT initiated. + */ + explicit Transaction(Database& aDatabase); + + /** + * @brief Begins the SQLite transaction with the specified behavior. + * + * @param[in] aDatabase the SQLite Database Connection + * @param[in] behavior the requested transaction behavior + * + * Exception is thrown in case of error, then the Transaction is NOT initiated. + */ + explicit Transaction(Database& aDatabase, TransactionBehavior behavior); + + // Transaction is non-copyable + Transaction(const Transaction&) = delete; + Transaction& operator=(const Transaction&) = delete; + + /** + * @brief Safely rollback the transaction if it has not been committed. + */ + ~Transaction(); + + /** + * @brief Commit the transaction. + */ + void commit(); + +private: + Database& mDatabase; ///< Reference to the SQLite Database Connection + bool mbCommited; ///< True when commit has been called +}; + + +} // namespace SQLite diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/Utils.h b/3rdparty/sqlitecpp/source/SQLiteCpp/Utils.h new file mode 100644 index 0000000..f053b3f --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/Utils.h @@ -0,0 +1,31 @@ +/** + * @file Utils.h + * @ingroup SQLiteCpp + * @brief Definition of the SQLITECPP_PURE_FUNC macro. + * + * Copyright (c) 2012-2020 Sebastien Rombauts ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +// macro taken from https://github.com/nemequ/hedley/blob/master/hedley.h that was in public domain at this time +#if defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) ||\ +(defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1600) ||\ +(defined(__ARMCC_VERSION) && __ARMCC_VERSION > 4010000) ||\ +(\ + defined(__TI_COMPILER_VERSION__) && (\ + __TI_COMPILER_VERSION__ > 8003000 ||\ + (__TI_COMPILER_VERSION__ > 7003000 && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\ + )\ +) +#if defined(__has_attribute) +#if !defined(SQLITECPP_PURE_FUNC) && __has_attribute(pure) +#define SQLITECPP_PURE_FUNC __attribute__((pure)) +#endif +#endif +#endif +#if !defined(SQLITECPP_PURE_FUNC) +#define SQLITECPP_PURE_FUNC +#endif diff --git a/3rdparty/sqlitecpp/source/SQLiteCpp/VariadicBind.h b/3rdparty/sqlitecpp/source/SQLiteCpp/VariadicBind.h new file mode 100644 index 0000000..e82b436 --- /dev/null +++ b/3rdparty/sqlitecpp/source/SQLiteCpp/VariadicBind.h @@ -0,0 +1,98 @@ +/** + * @file VariadicBind.h + * @ingroup SQLiteCpp + * @brief Convenience function for Statement::bind(...) + * + * Copyright (c) 2016 Paul Dreik ([email protected]) + * Copyright (c) 2016-2021 Sebastien Rombauts ([email protected]) + * Copyright (c) 2019 Maximilian Bachmann ([email protected]) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include <SQLiteCpp/Statement.h> + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 +#include <tuple> +#endif // c++14 + +/// @cond +#include <utility> +#include <initializer_list> + +namespace SQLite +{ +/// @endcond + +/** + * \brief Convenience function for calling Statement::bind(...) once for each argument given. + * + * This takes care of incrementing the index between each calls to bind. + * + * This feature requires a c++11 capable compiler. + * + * \code{.cpp} + * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC<?"); + * SQLite::bind(stm,a,b,c); + * //...is equivalent to + * stm.bind(1,a); + * stm.bind(2,b); + * stm.bind(3,c); + * \endcode + * @param query statement + * @param args zero or more args to bind. + */ +template<class ...Args> +void bind(SQLite::Statement& query, const Args& ... args) +{ + int pos = 0; + (void)std::initializer_list<int>{ + ((void)query.bind(++pos, std::forward<decltype(args)>(args)), 0)... + }; +} + +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 + +/** + * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, + * by forwarding them to the variadic template + * + * This feature requires a c++14 capable compiler. + * + * \code{.cpp} + * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC<?"); + * SQLite::bind(stm, std::make_tuple(a, b, c)); + * //...is equivalent to + * stm.bind(1,a); + * stm.bind(2,b); + * stm.bind(3,c); + * \endcode + * @param query statement + * @param tuple tuple with values to bind + */ +template <typename ... Types> +void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple) +{ + bind(query, tuple, std::index_sequence_for<Types...>()); +} + +/** + * \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple, + * by forwarding them to the variadic template. This function is just needed to convert the tuples + * to parameter packs + * + * This feature requires a c++14 capable compiler. + * + * @param query statement + * @param tuple tuple with values to bind + */ +template <typename ... Types, std::size_t ... Indices> +void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple, std::index_sequence<Indices...>) +{ + bind(query, std::get<Indices>(tuple)...); +} +#endif // c++14 + +} // namespace SQLite |