aboutsummaryrefslogtreecommitdiff
path: root/3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp')
-rw-r--r--3rdparty/sqlitecpp/source/SQLiteCpp/Database.cpp452
1 files changed, 452 insertions, 0 deletions
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