/* * @file Database.cpp * @author Mateusz Piesta (mateusz.piesta@mudita.com) * @date 24.05.19 * @brief * @copyright Copyright (C) 2019 mudita.com * @details */ #include "Database.hpp" #include "log/log.hpp" #include "vfs.hpp" #include #include /* Declarations *********************/ extern sqlite3_vfs *sqlite3_ecophonevfs(void); extern "C" { int sqlite3_os_init(void) { /* ** The following macro defines an initializer for an sqlite3_vfs object. ** The name of the VFS is NAME. The pAppData is a pointer to a pointer ** to the "finder" function. (pAppData is a pointer to a pointer because ** silly C90 rules prohibit a void* from being cast to a function pointer ** and so we have to go through the intermediate pointer to avoid problems ** when compiling with -pedantic-errors on GCC.) ** ** The FINDER parameter to this macro is the name of the pointer to the ** finder-function. The finder-function returns a pointer to the ** sqlite_io_methods object that implements the desired locking ** behaviors. See the division above that contains the IOMETHODS ** macro for addition information on finder-functions. ** ** Most finders simply return a pointer to a fixed sqlite3_io_methods ** object. But the "autolockIoFinder" available on MacOSX does a little ** more than that; it looks at the filesystem type that hosts the ** database file and tries to choose an locking method appropriate for ** that filesystem time. */ sqlite3_vfs_register(sqlite3_ecophonevfs(), 1); return SQLITE_OK; } /* ** Shutdown the operating system interface. ** ** Some operating systems might need to do some cleanup in this routine, ** to release dynamically allocated objects. But not on unix. ** This routine is a no-op for unix. */ int sqlite3_os_end(void) { return SQLITE_OK; } /* Internal Defines ***********************/ void errorLogCallback(void *pArg, int iErrCode, const char *zMsg) { LOG_ERROR("(%d) %s\n", iErrCode, zMsg); } } Database::Database(const char *name) : dbConnection(nullptr), dbName(name), isInitialized(false) { auto rc = sqlite3_open(name, &dbConnection); if (rc != SQLITE_OK) { LOG_ERROR("SQLITE INITIALIZATION ERROR! %d", rc); } assert(rc == SQLITE_OK); } Database::~Database() { sqlite3_close(dbConnection); } void Database::Initialize() { sqlite3_config( SQLITE_CONFIG_LOG, errorLogCallback, (void *)1); //(void*)1 is taken from official SQLITE examples and it appears that it ends variable args list sqlite3_initialize(); } void Database::Deinitialize() { sqlite3_shutdown(); } bool Database::Execute(const char *format, ...) { if (!format) { return false; } va_list ap; char *szQuery = static_cast(sqlite3_malloc(maxQueryLen)); va_start(ap, format); sqlite3_vsnprintf(maxQueryLen, (char *)szQuery, format, ap); va_end(ap); int result = sqlite3_exec(dbConnection, szQuery, NULL, NULL, NULL); if (result != SQLITE_OK) LOG_ERROR("Execution of \'%s\' failed with %d", szQuery, result); sqlite3_free(szQuery); return result != SQLITE_OK ? false : true; } std::unique_ptr Database::Query(const char *format, ...) { if (!format) { return nullptr; } va_list ap; char *szQuery = static_cast(sqlite3_malloc(maxQueryLen)); va_start(ap, format); szQuery[0] = 0; sqlite3_vsnprintf(maxQueryLen, szQuery, format, ap); va_end(ap); auto queryResult = std::make_unique(); int result = sqlite3_exec(dbConnection, szQuery, queryCallback, queryResult.get(), NULL); if (result != SQLITE_OK) { LOG_ERROR("SQL query \'%s\' failed selecting : %d", szQuery, result); return nullptr; } sqlite3_free(szQuery); return queryResult; } int Database::queryCallback(void *usrPtr, int count, char **data, char **columns) { QueryResult *db = reinterpret_cast(usrPtr); std::vector row; for (uint32_t i = 0; i < (uint32_t)count; i++) { try { row.push_back(Field{data[i]}); } catch (...) { LOG_FATAL("Error on: %" PRIu32 " %s", i, data[i]); } } db->AddRow(row); return 0; } uint32_t Database::GetLastInsertRowID() { return sqlite3_last_insert_rowid(dbConnection); }