diff --git a/framework/areg/persist/IEDatabaseEngine.hpp b/framework/areg/persist/IEDatabaseEngine.hpp index 89d108a22..51e2189e1 100644 --- a/framework/areg/persist/IEDatabaseEngine.hpp +++ b/framework/areg/persist/IEDatabaseEngine.hpp @@ -87,6 +87,11 @@ class AREG_API IEDatabaseEngine **/ virtual bool commit(bool doCommit) = 0; + /** + * \brief Rolls back the database changes and returns true if succeeded. + **/ + virtual bool rollback(void) = 0; + ////////////////////////////////////////////////////////////////////////// // Forbidden calls. ////////////////////////////////////////////////////////////////////////// diff --git a/framework/aregextend.vcxproj b/framework/aregextend.vcxproj index 271a3f20c..a9e2e1752 100644 --- a/framework/aregextend.vcxproj +++ b/framework/aregextend.vcxproj @@ -47,6 +47,8 @@ + + @@ -70,6 +72,8 @@ + + diff --git a/framework/aregextend.vcxproj.filters b/framework/aregextend.vcxproj.filters index b2c1b6d68..90546d8d9 100644 --- a/framework/aregextend.vcxproj.filters +++ b/framework/aregextend.vcxproj.filters @@ -72,6 +72,12 @@ Source Files + + Source Files + + + Source Files + @@ -133,6 +139,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/framework/aregextend/db/LogSqliteDatabase.hpp b/framework/aregextend/db/LogSqliteDatabase.hpp index c047254ba..48d325630 100644 --- a/framework/aregextend/db/LogSqliteDatabase.hpp +++ b/framework/aregextend/db/LogSqliteDatabase.hpp @@ -21,6 +21,30 @@ #include "areg/logging/IELogDatabaseEngine.hpp" #include "areg/base/String.hpp" +#if 0 +#include + +enum eLogItemType +{ + ItemUnknown = 0 //!< Unknown log item type + , ItemInstance //!< Log instance item + , ItemThread //!< Log thread item + , ItemScope //!< Log scope item + , ItemPriority //!< Log priority item +}; + +struct sLogItem +{ + ITEM_ID itemId { 0 }; // itemList{ }; //!< The list of log items +}; +#endif ////////////////////////////////////////////////////////////////////////// // LogSqliteDatabase class declaration ////////////////////////////////////////////////////////////////////////// @@ -199,6 +223,39 @@ class LogSqliteDatabase : public IELogDatabaseEngine **/ virtual bool logScopeDeactivate(const ITEM_ID & cookie, unsigned int scopeId, const DateTime & timestamp) override; + /** + * \brief Rolls back the database changes and returns true if succeeded. + **/ + virtual bool rollback(void) override; + +////////////////////////////////////////////////////////////////////////// +// Attributes and operations +////////////////////////////////////////////////////////////////////////// +#if 0 + std::vector getLogInstanceNames(void) const; + + std::vector getLogInstances(void) const; + + std::vector getLogThreadNames(void) const; + + std::vector getLogThreads(void) const; + + std::vector getLogScopeNames(void) const; + + std::vector getLogScopes(void) const; + + std::vector getPriorityNames(void) const; + + std::vector getLogInstScopes(ITEM_ID instId) const; + + std::vector getLodMessages(void) const; + + std::vector getLodInstMessages(ITEM_ID instId) const; + + std::vector getLodScopeMessages(ITEM_ID instId, uint32_t scopeId) const; + +#endif + ////////////////////////////////////////////////////////////////////////// // Hidden methods ////////////////////////////////////////////////////////////////////////// diff --git a/framework/aregextend/db/SqliteDatabase.hpp b/framework/aregextend/db/SqliteDatabase.hpp index 8c79f7229..44ac01520 100644 --- a/framework/aregextend/db/SqliteDatabase.hpp +++ b/framework/aregextend/db/SqliteDatabase.hpp @@ -29,6 +29,7 @@ **/ class SqliteDatabase : public IEDatabaseEngine { + friend class SqliteStatement; ////////////////////////////////////////////////////////////////////////// // Constructors / Destructor ////////////////////////////////////////////////////////////////////////// @@ -111,6 +112,11 @@ class SqliteDatabase : public IEDatabaseEngine **/ virtual bool commit(bool doCommit) override; + /** + * \brief Rolls back the database changes and returns true if succeeded. + **/ + virtual bool rollback(void) override; + ////////////////////////////////////////////////////////////////////////// // Hidden methods ////////////////////////////////////////////////////////////////////////// diff --git a/framework/aregextend/db/SqliteRow.hpp b/framework/aregextend/db/SqliteRow.hpp new file mode 100644 index 000000000..9b673bd7e --- /dev/null +++ b/framework/aregextend/db/SqliteRow.hpp @@ -0,0 +1,204 @@ +#ifndef AREG_AREGEXTEND_DB_SQLITEROW_HPP +#define AREG_AREGEXTEND_DB_SQLITEROW_HPP +/************************************************************************ + * This file is part of the AREG SDK core engine. + * AREG SDK is dual-licensed under Free open source (Apache version 2.0 + * License) and Commercial (with various pricing models) licenses, depending + * on the nature of the project (commercial, research, academic or free). + * You should have received a copy of the AREG SDK license description in LICENSE.txt. + * If not, please contact to info[at]aregtech.com + * + * \copyright (c) 2017-2023 Aregtech UG. All rights reserved. + * \file aregextend/db/SqliteRow.hpp + * \author Artak Avetyan + * \ingroup AREG platform, extended library, SQLite row object to get results. + ************************************************************************/ + +/************************************************************************ + * Include files. + ************************************************************************/ +#include "areg/base/GEGlobal.h" +#include "areg/base/String.hpp" + +/************************************************************************ + * Dependencies + ************************************************************************/ +class SqliteStatement; + +////////////////////////////////////////////////////////////////////////// +// SqliteRow class declaration +////////////////////////////////////////////////////////////////////////// +/** + * \brief Represents a single row of results from a SQLite query. + * Provides access to column values and metadata for the current row. + **/ +class SqliteRow +{ +////////////////////////////////////////////////////////////////////////// +// Constructors / operators. +////////////////////////////////////////////////////////////////////////// +public: + /** + * \brief Default constructor. Initializes an invalid row. + */ + SqliteRow(void); + + /** + * \brief Constructs a SqliteRow from a SqliteStatement. + * \param statement Reference to the SqliteStatement object. + */ + SqliteRow(SqliteStatement& statement); + + /** + * \brief Constructs a SqliteRow from a raw statement pointer. + * \param statement Pointer to the underlying SQLite statement. + */ + SqliteRow(void* statement); + + /** + * \brief Copy constructor. + * \param src The source SqliteRow to copy from. + */ + SqliteRow(const SqliteRow& src); + + /** + * \brief Move constructor. + * \param src The source SqliteRow to move from. + */ + SqliteRow(SqliteRow&& src); + + /** + * \brief Destructor. Defaulted. + */ + ~SqliteRow(void) = default; + + /** + * \brief Copy assignment operator. + * \param src The source SqliteRow to copy from. + * \return Reference to this object. + */ + SqliteRow& operator = (const SqliteRow& src); + + /** + * \brief Move assignment operator. + * \param src The source SqliteRow to move from. + * \return Reference to this object. + */ + SqliteRow& operator = (SqliteRow&& src); + +////////////////////////////////////////////////////////////////////////// +// Attributes and operations +////////////////////////////////////////////////////////////////////////// +public: + /** + * \brief Checks if the row is valid (i.e., associated with a statement). + * \return True if valid, false otherwise. + */ + inline bool isValid(void) const; + + /** + * \brief Retrieves the integer value of the specified column. + * \param column The 0-based column index. + * \return The integer value of the column. + */ + int getInt(int column) const; + + /** + * \brief Retrieves the 64-bit integer value of the specified column. + * \param column The 0-based column index. + * \return The 64-bit integer value of the column. + */ + int64_t getInt64(int column) const; + + /** + * \brief Retrieves the double value of the specified column. + * \param column The 0-based column index. + * \return The double value of the column. + */ + double getDouble(int column) const; + + /** + * \brief Retrieves the text value of the specified column. + * \param column The 0-based column index. + * \return The string value of the column. + */ + String getText(int column) const; + + /** + * \brief Checks if the specified column is NULL. + * \param column The 0-based column index. + * \return True if the column is NULL, false otherwise. + */ + bool isNull(int column) const; + + /** + * \brief Checks if the specified column index is valid. + * \param column The 0-based column index. + * \return True if the column index is valid, false otherwise. + */ + bool isColumnValid(int column) const; + + /** + * \brief Checks if the specified column contains a string value. + * \param column The 0-based column index. + * \return True if the column is a string, false otherwise. + */ + bool isString(int column) const; + + /** + * \brief Checks if the specified column contains a 32-bit integer value. + * \param column The 0-based column index. + * \return True if the column is a 32-bit integer, false otherwise. + */ + bool isInteger(int column) const; + + /** + * \brief Checks if the specified column contains a 64-bit integer value. + * \param column The 0-based column index. + * \return True if the column is a 64-bit integer, false otherwise. + */ + bool isInteger64(int column) const; + + /** + * \brief Checks if the specified column contains a double value. + * \param column The 0-based column index. + * \return True if the column is a double, false otherwise. + */ + bool isDouble(int column) const; + + /** + * \brief Returns the number of columns in the row. + * \return The number of columns. + */ + int getColumnCount(void) const; + + /** + * \brief Returns the name of the specified column. + * \param column The 0-based column index. + * \return The name of the column. + */ + String getColumnName(int column) const; + + /** + * \brief Returns the index of the column with the specified name. + * \param columnName The name of the column. + * \return The 0-based index of the column, or -1 if not found. + */ + int getColumnIndex(const String& columnName) const; + +////////////////////////////////////////////////////////////////////////// +// Member variables +////////////////////////////////////////////////////////////////////////// +protected: + /** + * \brief Pointer to the underlying SQLite statement. + */ + void* mStatement; +}; + +inline bool SqliteRow::isValid(void) const +{ + return (mStatement != nullptr); +} + +#endif // AREG_AREGEXTEND_DB_SQLITEROW_HPP diff --git a/framework/aregextend/db/SqliteStatement.hpp b/framework/aregextend/db/SqliteStatement.hpp new file mode 100644 index 000000000..e7d1c612a --- /dev/null +++ b/framework/aregextend/db/SqliteStatement.hpp @@ -0,0 +1,308 @@ +#ifndef AREG_AREGEXTEND_DB_SQLITESTATEMENT_HPP +#define AREG_AREGEXTEND_DB_SQLITESTATEMENT_HPP +/************************************************************************ + * This file is part of the AREG SDK core engine. + * AREG SDK is dual-licensed under Free open source (Apache version 2.0 + * License) and Commercial (with various pricing models) licenses, depending + * on the nature of the project (commercial, research, academic or free). + * You should have received a copy of the AREG SDK license description in LICENSE.txt. + * If not, please contact to info[at]aregtech.com + * + * \copyright (c) 2017-2023 Aregtech UG. All rights reserved. + * \file aregextend/db/SqliteStatement.hpp + * \author Artak Avetyan + * \ingroup AREG platform, extended library, SQLite statement object. + ************************************************************************/ + +/************************************************************************ + * Include files. + ************************************************************************/ +#include "areg/base/GEGlobal.h" +#include "areg/base/String.hpp" +#include "aregextend/db/SqliteRow.hpp" + +class SqliteDatabase; + +////////////////////////////////////////////////////////////////////////// +// SqliteStatement class declaration +////////////////////////////////////////////////////////////////////////// + +/** + * /brief This class represents a prepared SQL statement that can be executed against a SqliteDatabase. + **/ +class SqliteStatement +{ + friend class SqliteRow; + +////////////////////////////////////////////////////////////////////////// +// Internal types and constants +////////////////////////////////////////////////////////////////////////// +public: + /** + * \brief Enum representing the type of a column in the result set. + */ + enum class eColumnType + { + ColumnUnknown = 0 //!< Unknown column type. + , ColumnInteger //!< 32-bit integer column. + , ColumnInteger64 //!< 64-bit integer column. + , ColumnDouble //!< Double-precision floating point column. + , ColumnText //!< Text (string) column. + , ColumnBlob //!< Binary large object column. + , ColumnNull //!< Null column. + }; + +////////////////////////////////////////////////////////////////////////// +// Constructors / Destructor +////////////////////////////////////////////////////////////////////////// +public: + /** + * \brief Constructs a SqliteStatement and prepares the given SQL statement. + * \param db Reference to the SqliteDatabase object. + * \param sql The SQL statement to prepare. + */ + SqliteStatement(SqliteDatabase& db, const String& sql); + + /** + * \brief Constructs a SqliteStatement associated with the given database. + * The statement is not prepared until prepare() is called. + * \param db Reference to the SqliteDatabase object. + */ + SqliteStatement(SqliteDatabase& db); + + /** + * \brief Destructor. Finalizes the statement and releases resources. + */ + ~SqliteStatement(void); + +////////////////////////////////////////////////////////////////////////// +// Attributes and operations +////////////////////////////////////////////////////////////////////////// +public: + /** + * \brief Prepares the SQL statement for execution. + * \param sql The SQL statement to prepare. + * \return True if the statement was prepared successfully, false otherwise. + */ + bool prepare(const String& sql); + + /** + * \brief Executes the prepared statement (for non-SELECT statements). + * \return True if execution was successful, false otherwise. + */ + bool execute(void); + + /** + * \brief Advances to the next row in the result set. + * \return True if a new row is available, false if there are no more rows. + */ + bool next(void); + + /** + * \brief Resets the statement to its initial state, ready for re-execution. + */ + void reset(void); + + /** + * \brief Finalizes the statement and releases associated resources. + */ + void finalize(void); + + /** + * \brief Checks if the statement is prepared and ready for execution. + * \return True if the statement is prepared, false otherwise. + */ + inline bool isValid(void) const; + + /** + * \brief Returns the current row position in the result set. + * The first row starts with 1. Return 0 if SQL statement is not prepared yet. + **/ + inline uint32_t getRowPos(void) const; + + /** + * \brief Binds an integer value to the specified parameter index. + * \param index The 1-based parameter index. + * \param value The integer value to bind. + * \return True if binding was successful, false otherwise. + */ + bool bindInt(int index, int value); + + /** + * \brief Binds a 64-bit integer value to the specified parameter index. + * \param index The 1-based parameter index. + * \param value The 64-bit integer value to bind. + * \return True if binding was successful, false otherwise. + */ + bool bindInt64(int index, int64_t value); + + /** + * \brief Binds a double value to the specified parameter index. + * \param index The 1-based parameter index. + * \param value The double value to bind. + * \return True if binding was successful, false otherwise. + */ + bool bindDouble(int index, double value); + + /** + * \brief Binds a text value to the specified parameter index. + * \param index The 1-based parameter index. + * \param value The string value to bind. + * \return True if binding was successful, false otherwise. + */ + bool bindText(int index, const String& value); + + /** + * \brief Binds a NULL value to the specified parameter index. + * \param index The 1-based parameter index. + * \return True if binding was successful, false otherwise. + */ + bool bindNull(int index); + + /** + * \brief Clears all parameter bindings for the prepared statement. + */ + void clearBindings(void); + + /** + * \brief Retrieves the integer value of the specified column in the current row. + * \param column The 0-based column index. + * \return The integer value of the column. + */ + int getInt(int column) const; + + /** + * \brief Retrieves the 64-bit integer value of the specified column in the current row. + * \param column The 0-based column index. + * \return The 64-bit integer value of the column. + */ + int64_t getInt64(int column) const; + + /** + * \brief Retrieves the double value of the specified column in the current row. + * \param column The 0-based column index. + * \return The double value of the column. + */ + double getDouble(int column) const; + + /** + * \brief Retrieves the text value of the specified column in the current row. + * \param column The 0-based column index. + * \return The string value of the column. + */ + String getText(int column) const; + + /** + * \brief Checks if the specified column in the current row is NULL. + * \param column The 0-based column index. + * \return True if the column is NULL, false otherwise. + */ + bool isNull(int column) const; + + /** + * \brief Checks if the specified column index is valid for the current result set. + * \param column The 0-based column index. + * \return True if the column index is valid, false otherwise. + */ + bool isColumnValid(int column) const; + + /** + * \brief Checks if the specified column contains a string value. + * \param column The 0-based column index. + * \return True if the column is a string, false otherwise. + */ + bool isString(int column) const; + + /** + * \brief Checks if the specified column contains a 32-bit integer value. + * \param column The 0-based column index. + * \return True if the column is a 32-bit integer, false otherwise. + */ + bool isInteger(int column) const; + + /** + * \brief Checks if the specified column contains a 64-bit integer value. + * \param column The 0-based column index. + * \return True if the column is a 64-bit integer, false otherwise. + */ + bool isInteger64(int column) const; + + /** + * \brief Checks if the specified column contains a double value. + * \param column The 0-based column index. + * \return True if the column is a double, false otherwise. + */ + bool isDouble(int column) const; + + /** + * \brief Returns the number of columns in the result set. + * \return The number of columns. + */ + int getColumnCount(void) const; + + /** + * \brief Returns the name of the specified column. + * \param column The 0-based column index. + * \return The name of the column. + */ + String getColumnName(int column) const; + + /** + * \brief Returns the index of the column with the specified name. + * \param columnName The name of the column. + * \return The 0-based index of the column, or -1 if not found. + */ + int getColumnIndex(const String& columnName) const; + + /** + * \brief Returns the type of the specified column. + * \param column The 0-based column index. + * \return The column type as eColumnType. + */ + SqliteStatement::eColumnType getColumnType(int column) const; + + /** + * \brief Returns a SqliteRow object representing the current row. + * \return The current SqliteRow. + */ + SqliteRow row() const; + + /** + * \brief Advances to the next row and returns a SqliteRow object for it. + * \return The next SqliteRow. + */ + SqliteRow nextRow(void) const; + + /** + * \brief Advances to the next row in the result set (const overload). + * \return True if a new row is available, false otherwise. + */ + bool next(void) const; + +////////////////////////////////////////////////////////////////////////// +// Member variables +////////////////////////////////////////////////////////////////////////// +protected: + SqliteDatabase& mDatabase; //!< The SQLite database object, which owns this statement. + void * mStatement; //!< The SQLite statement object. + uint32_t mRowPos; //!< The current row position. + +////////////////////////////////////////////////////////////////////////// +// Forbidden calls +////////////////////////////////////////////////////////////////////////// + SqliteStatement(void) = delete; //!< Default constructor is not allowed. + DECLARE_NOCOPY_NOMOVE(SqliteStatement); //!< No copy or move allowed. +}; + +inline bool SqliteStatement::isValid(void) const +{ + return (mStatement != nullptr); +} + + +inline uint32_t SqliteStatement::getRowPos(void) const +{ + return mRowPos; +} + +#endif // AREG_AREGEXTEND_DB_SQLITESTATEMENT_HPP diff --git a/framework/aregextend/db/private/CMakeLists.txt b/framework/aregextend/db/private/CMakeLists.txt index 7e3985d04..5ef04a3b0 100644 --- a/framework/aregextend/db/private/CMakeLists.txt +++ b/framework/aregextend/db/private/CMakeLists.txt @@ -1,4 +1,6 @@ macro_add_source(aregextend_SRC "${AREG_FRAMEWORK}" aregextend/db/private/LogSqliteDatabase.cpp aregextend/db/private/SqliteDatabase.cpp + aregextend/db/private/SqliteRow.cpp + aregextend/db/private/SqliteStatement.cpp ) diff --git a/framework/aregextend/db/private/LogSqliteDatabase.cpp b/framework/aregextend/db/private/LogSqliteDatabase.cpp index 0104621c6..083480108 100644 --- a/framework/aregextend/db/private/LogSqliteDatabase.cpp +++ b/framework/aregextend/db/private/LogSqliteDatabase.cpp @@ -584,3 +584,65 @@ bool LogSqliteDatabase::logScopeDeactivate(const ITEM_ID& cookie, unsigned int s ); return _execute(sql); } + +bool LogSqliteDatabase::rollback(void) +{ + return commit(false); +} + +#if 0 +std::vector LogSqliteDatabase::getLogInstanceNames(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogInstances(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogThreadNames(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogThreads(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogScopeNames(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogScopes(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getPriorityNames(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLogInstScopes(ITEM_ID instId) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLodMessages(void) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLodInstMessages(ITEM_ID instId) const +{ + return std::vector(); +} + +std::vector LogSqliteDatabase::getLodScopeMessages(ITEM_ID instId, uint32_t scopeId) const +{ + return std::vector(); +} +#endif diff --git a/framework/aregextend/db/private/SqliteDatabase.cpp b/framework/aregextend/db/private/SqliteDatabase.cpp index 4b4ef9b0a..55661a08a 100644 --- a/framework/aregextend/db/private/SqliteDatabase.cpp +++ b/framework/aregextend/db/private/SqliteDatabase.cpp @@ -27,7 +27,18 @@ #include "sqlite3/amalgamation/sqlite3.h" #endif // defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) +namespace +{ + inline sqlite3* _sqlite(void* dbObject) + { + return reinterpret_cast(dbObject); + } + inline sqlite3** _sqlite(void** dbObject) + { + return reinterpret_cast(dbObject); + } +} SqliteDatabase::SqliteDatabase(void) : IEDatabaseEngine ( ) @@ -43,7 +54,7 @@ SqliteDatabase::SqliteDatabase(const String& dbPath, bool open) { if (open && (mDbPath.isEmpty() == false)) { - if (::sqlite3_open(mDbPath.getString(), reinterpret_cast(&mDbObject)) != SQLITE_OK) + if (::sqlite3_open(mDbPath.getString(), _sqlite(&mDbObject)) != SQLITE_OK) { _close(); } @@ -60,7 +71,7 @@ inline bool SqliteDatabase::_open(const String& dbPath) bool result{ true }; _close(); mDbPath = dbPath.isEmpty() == false ? File::normalizePath(dbPath) : mDbPath; - if (SQLITE_OK != ::sqlite3_open(mDbPath.getString(), reinterpret_cast(&mDbObject))) + if (SQLITE_OK != ::sqlite3_open(mDbPath.getString(), _sqlite(&mDbObject))) { _close(); result = false; @@ -73,7 +84,7 @@ inline void SqliteDatabase::_close(void) { if (mDbObject != nullptr) { - ::sqlite3_close(reinterpret_cast(mDbObject)); + ::sqlite3_close(_sqlite(mDbObject)); mDbObject = nullptr; } } @@ -98,7 +109,7 @@ bool SqliteDatabase::execute(const String& sql) bool result{ false }; if (mDbObject != nullptr) { - result = SQLITE_OK == ::sqlite3_exec(reinterpret_cast(mDbObject), sql.getString(), nullptr, nullptr, nullptr); + result = SQLITE_OK == ::sqlite3_exec(_sqlite(mDbObject), sql.getString(), nullptr, nullptr, nullptr); } return result; @@ -108,7 +119,7 @@ bool SqliteDatabase::begin(void) { constexpr std::string_view sqlBegin{ "BEGIN TRANSACTION;" }; - return (mDbObject != nullptr ? SQLITE_OK == ::sqlite3_exec(reinterpret_cast(mDbObject), sqlBegin.data(), nullptr, nullptr, nullptr) : false); + return (mDbObject != nullptr ? SQLITE_OK == ::sqlite3_exec(_sqlite(mDbObject), sqlBegin.data(), nullptr, nullptr, nullptr) : false); } bool SqliteDatabase::commit(bool doCommit) @@ -116,5 +127,10 @@ bool SqliteDatabase::commit(bool doCommit) constexpr std::string_view sqlCommit{ "COMMIT;" }; constexpr std::string_view sqlRoolback{ "ROLLBACK;" }; - return (mDbObject != nullptr ? SQLITE_OK == ::sqlite3_exec(reinterpret_cast(mDbObject), doCommit ? sqlCommit.data() : sqlRoolback.data(), nullptr, nullptr, nullptr) : false); + return (mDbObject != nullptr ? SQLITE_OK == ::sqlite3_exec(_sqlite(mDbObject), doCommit ? sqlCommit.data() : sqlRoolback.data(), nullptr, nullptr, nullptr) : false); +} + +bool SqliteDatabase::rollback(void) +{ + return commit(false); } diff --git a/framework/aregextend/db/private/SqliteRow.cpp b/framework/aregextend/db/private/SqliteRow.cpp new file mode 100644 index 000000000..2dc45118a --- /dev/null +++ b/framework/aregextend/db/private/SqliteRow.cpp @@ -0,0 +1,167 @@ +/************************************************************************ + * This file is part of the AREG SDK core engine. + * AREG SDK is dual-licensed under Free open source (Apache version 2.0 + * License) and Commercial (with various pricing models) licenses, depending + * on the nature of the project (commercial, research, academic or free). + * You should have received a copy of the AREG SDK license description in LICENSE.txt. + * If not, please contact to info[at]aregtech.com + * + * \copyright (c) 2017-2023 Aregtech UG. All rights reserved. + * \file aregextend/db/SqliteRow.cpp + * \author Artak Avetyan + * \ingroup AREG platform, extended library, SQLite row object to get results. + ************************************************************************/ + +#include "aregextend/db/SqliteRow.hpp" +#include "aregextend/db/SqliteStatement.hpp" + +#if defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + #include +#else // defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + #include "sqlite3/amalgamation/sqlite3.h" +#endif // defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + +namespace +{ + inline sqlite3_stmt* _sqlite_stmt(void* stmtObject) + { + return reinterpret_cast(stmtObject); + } +} + +SqliteRow::SqliteRow(void) + : mStatement(nullptr) +{ +} + +SqliteRow::SqliteRow(SqliteStatement& statement) + : mStatement(statement.mStatement) +{ +} + +SqliteRow::SqliteRow(void* statement) + : mStatement(statement) +{ +} + +SqliteRow::SqliteRow(const SqliteRow& src) + : mStatement(src.mStatement) +{ +} + +SqliteRow::SqliteRow(SqliteRow&& src) + : mStatement(src.mStatement) +{ + src.mStatement = nullptr; // Transfer ownership +} + +SqliteRow& SqliteRow::operator = (const SqliteRow& src) +{ + mStatement = src.mStatement; + return (*this); +} + +SqliteRow& SqliteRow::operator = (SqliteRow&& src) +{ + mStatement = src.mStatement; + src.mStatement = nullptr; // Transfer ownership + return (*this); +} + +int SqliteRow::getInt(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_int(_sqlite_stmt(mStatement), column); +} + +int64_t SqliteRow::getInt64(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_int64(_sqlite_stmt(mStatement), column); +} + +double SqliteRow::getDouble(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_double(_sqlite_stmt(mStatement), column); +} + +String SqliteRow::getText(int column) const +{ + return String(reinterpret_cast(sqlite3_column_text(_sqlite_stmt(mStatement), column))); +} + +bool SqliteRow::isNull(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_NULL); +} + +bool SqliteRow::isColumnValid(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) != SQLITE_NULL); +} + +bool SqliteRow::isString(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_TEXT); +} + +bool SqliteRow::isInteger(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_INTEGER); +} + +bool SqliteRow::isInteger64(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_INTEGER); +} + +bool SqliteRow::isDouble(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_FLOAT); +} + +int SqliteRow::getColumnCount(void) const +{ + ASSERT(isValid()); + return sqlite3_column_count(_sqlite_stmt(mStatement)); +} + +String SqliteRow::getColumnName(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + const char* columnName = sqlite3_column_name(_sqlite_stmt(mStatement), column); + return String((columnName != nullptr) ? columnName : String::EmptyString); +} + +int SqliteRow::getColumnIndex(const String& columnName) const +{ + ASSERT(isValid()); + ASSERT(!columnName.isEmpty()); + int columnCount = getColumnCount(); + for (int index = 0; index < columnCount; ++index) + { + if (getColumnName(index) == columnName) + { + return index; + } + } + + return NECommon::INVALID_INDEX; // Column not found +} diff --git a/framework/aregextend/db/private/SqliteStatement.cpp b/framework/aregextend/db/private/SqliteStatement.cpp new file mode 100644 index 000000000..980bf2c7b --- /dev/null +++ b/framework/aregextend/db/private/SqliteStatement.cpp @@ -0,0 +1,253 @@ +/************************************************************************ + * This file is part of the AREG SDK core engine. + * AREG SDK is dual-licensed under Free open source (Apache version 2.0 + * License) and Commercial (with various pricing models) licenses, depending + * on the nature of the project (commercial, research, academic or free). + * You should have received a copy of the AREG SDK license description in LICENSE.txt. + * If not, please contact to info[at]aregtech.com + * + * \copyright (c) 2017-2023 Aregtech UG. All rights reserved. + * \file aregextend/db/SqliteStatement.cpp + * \author Artak Avetyan + * \ingroup AREG platform, extended library, SQLite statement object. + ************************************************************************/ +#include "aregextend/db/SqliteStatement.hpp" +#include "aregextend/db/SqliteDatabase.hpp" + +#if defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + #include +#else // defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + #include "sqlite3/amalgamation/sqlite3.h" +#endif // defined(USE_SQLITE_PACKAGE) && (USE_SQLITE_PACKAGE != 0) + +namespace +{ + inline sqlite3* _sqlite(void* dbObject) + { + return reinterpret_cast(dbObject); + } + + inline sqlite3** _sqlite(void** dbObject) + { + return reinterpret_cast(dbObject); + } + + inline sqlite3_stmt* _sqlite_stmt(void* stmtObject) + { + return reinterpret_cast(stmtObject); + } + + inline sqlite3_stmt** _sqlite_stmt(void** stmtObject) + { + return reinterpret_cast(stmtObject); + } +} + +SqliteStatement::SqliteStatement(SqliteDatabase& db, const String& sql) + : mDatabase (db) + , mStatement(nullptr) + , mRowPos (0) +{ + if (db.isOperable()) + { + prepare(sql); + } +} + +SqliteStatement::SqliteStatement(SqliteDatabase& db) + : mDatabase (db) + , mStatement(nullptr) + , mRowPos (0) +{ +} + +SqliteStatement::~SqliteStatement(void) +{ + finalize(); +} + +bool SqliteStatement::prepare(const String& sql) +{ + bool result{ (mDatabase.isOperable() && !sql.isEmpty() && (SQLITE_OK == ::sqlite3_prepare_v2(_sqlite(mDatabase.mDbObject), sql.getString(), -1, _sqlite_stmt(&mStatement), nullptr))) }; + mRowPos = result ? 1 : 0; // Reset row position + return result; +} + +bool SqliteStatement::execute() +{ + return isValid() && (sqlite3_step(_sqlite_stmt(mStatement)) == SQLITE_DONE); +} + +bool SqliteStatement::next() +{ + bool result{ isValid() && (sqlite3_step(_sqlite_stmt(mStatement)) == SQLITE_ROW) }; + mRowPos += result ? 1 : 0; // Increment row position if a new row is available + return result; +} + +void SqliteStatement::reset() +{ + mRowPos = 0; // Reset row position + sqlite3_reset(_sqlite_stmt(mStatement)); +} + +void SqliteStatement::finalize() +{ + if (mStatement != nullptr) + { + sqlite3_finalize(_sqlite_stmt(mStatement)); + mStatement = nullptr; + mRowPos = 0; // Reset row position + } +} + +bool SqliteStatement::bindInt(int index, int value) +{ + return isValid() && (sqlite3_bind_int(_sqlite_stmt(mStatement), index, value) == SQLITE_OK); +} + +bool SqliteStatement::bindInt64(int index, int64_t value) +{ + return isValid() && (sqlite3_bind_int64(_sqlite_stmt(mStatement), index, value) == SQLITE_OK); +} + +bool SqliteStatement::bindDouble(int index, double value) +{ + return isValid() && (sqlite3_bind_double(_sqlite_stmt(mStatement), index, value) == SQLITE_OK); +} + +bool SqliteStatement::bindText(int index, const String& value) +{ + return isValid() && (sqlite3_bind_text(_sqlite_stmt(mStatement), index, value.getString(), -1, SQLITE_TRANSIENT) == SQLITE_OK); +} + +bool SqliteStatement::bindNull(int index) +{ + return isValid() && (sqlite3_bind_null(_sqlite_stmt(mStatement), index) == SQLITE_OK); +} + +void SqliteStatement::clearBindings(void) +{ + ASSERT(isValid()); + sqlite3_clear_bindings(_sqlite_stmt(mStatement)); +} + +int SqliteStatement::getInt(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_int(_sqlite_stmt(mStatement), column); +} + +int64_t SqliteStatement::getInt64(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_int64(_sqlite_stmt(mStatement), column); +} + +double SqliteStatement::getDouble(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return sqlite3_column_double(_sqlite_stmt(mStatement), column); +} + +String SqliteStatement::getText(int column) const +{ + return String(reinterpret_cast(sqlite3_column_text(_sqlite_stmt(mStatement), column))); +} + +bool SqliteStatement::isNull(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_NULL); +} + +bool SqliteStatement::isColumnValid(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) != SQLITE_NULL); +} + +bool SqliteStatement::isString(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_TEXT); +} + +bool SqliteStatement::isInteger(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_INTEGER); +} + +bool SqliteStatement::isInteger64(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_INTEGER); +} + +bool SqliteStatement::isDouble(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + return (sqlite3_column_type(_sqlite_stmt(mStatement), column) == SQLITE_FLOAT); +} + +int SqliteStatement::getColumnCount(void) const +{ + ASSERT(isValid()); + return sqlite3_column_count(_sqlite_stmt(mStatement)); +} + +String SqliteStatement::getColumnName(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + const char* columnName = sqlite3_column_name(_sqlite_stmt(mStatement), column); + return String((columnName != nullptr) ? columnName : String::EmptyString); +} + +int SqliteStatement::getColumnIndex(const String& columnName) const +{ + ASSERT(isValid()); + ASSERT(!columnName.isEmpty()); + int columnCount = getColumnCount(); + for (int index = 0; index < columnCount; ++index) + { + if (getColumnName(index) == columnName) + { + return index; + } + } + + return NECommon::INVALID_INDEX; // Column not found +} + +SqliteStatement::eColumnType SqliteStatement::getColumnType(int column) const +{ + ASSERT(isValid()); + ASSERT(column >= 0); + int type = sqlite3_column_type(_sqlite_stmt(mStatement), column); + switch (type) + { + case SQLITE_INTEGER: + return eColumnType::ColumnInteger; + case SQLITE_FLOAT: + return eColumnType::ColumnDouble; + case SQLITE_TEXT: + return eColumnType::ColumnText; + case SQLITE_BLOB: + return eColumnType::ColumnBlob; + case SQLITE_NULL: + return eColumnType::ColumnNull; + default: + return eColumnType::ColumnUnknown; + } +}