diff --git a/cbp/ModSQLite.cbp b/cbp/ModSQLite.cbp index ddea23e1..cf1d5a83 100644 --- a/cbp/ModSQLite.cbp +++ b/cbp/ModSQLite.cbp @@ -421,10 +421,19 @@ + + + + - + + + + + + diff --git a/modules/sqlite/Column.cpp b/modules/sqlite/Column.cpp index c9ab3635..0de6b220 100644 --- a/modules/sqlite/Column.cpp +++ b/modules/sqlite/Column.cpp @@ -2,14 +2,10 @@ #include "Column.hpp" #include "Connection.hpp" #include "Statement.hpp" -#include "Module.hpp" // ------------------------------------------------------------------------------------------------ #include -// ------------------------------------------------------------------------------------------------ -#include - // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Column.hpp b/modules/sqlite/Column.hpp index f3df6dd3..607dcd89 100644 --- a/modules/sqlite/Column.hpp +++ b/modules/sqlite/Column.hpp @@ -2,8 +2,7 @@ #define _SQSQLITE_COLUMN_HPP_ // ------------------------------------------------------------------------------------------------ -#include "Common.hpp" -#include "SqAPI.h" +#include "Handle/Statement.hpp" // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Common.cpp b/modules/sqlite/Common.cpp index bc2b6281..b716d28f 100644 --- a/modules/sqlite/Common.cpp +++ b/modules/sqlite/Common.cpp @@ -1,68 +1,15 @@ // ------------------------------------------------------------------------------------------------ #include "Common.hpp" -#include "Module.hpp" -#include "Connection.hpp" +#include "Handle/Connection.hpp" // ------------------------------------------------------------------------------------------------ -#include +#include #include #include -// ------------------------------------------------------------------------------------------------ -#include - // ------------------------------------------------------------------------------------------------ namespace SqMod { -// ------------------------------------------------------------------------------------------------ -static SQChar g_Buffer[4096]; // Common buffer to reduce memory allocations. - -// ------------------------------------------------------------------------------------------------ -SStr GetTempBuff() -{ - return g_Buffer; -} - -// ------------------------------------------------------------------------------------------------ -Uint32 GetTempBuffSize() -{ - return sizeof(g_Buffer); -} - -// ------------------------------------------------------------------------------------------------ -void SqThrowF(CSStr str, ...) -{ - // Initialize the argument list - va_list args; - va_start (args, str); - // Write the requested contents - if (std::vsnprintf(g_Buffer, sizeof(g_Buffer), str, args) < 0) - { - std::strcpy(g_Buffer, "Unknown error has occurred"); - } - // Release the argument list - va_end(args); - // Throw the exception with the resulted message - throw Sqrat::Exception(g_Buffer); -} - -// ------------------------------------------------------------------------------------------------ -CSStr FmtStr(CSStr str, ...) -{ - // Initialize the argument list - va_list args; - va_start (args, str); - // Write the requested contents - if (std::vsnprintf(g_Buffer, sizeof(g_Buffer), str, args) < 0) - { - g_Buffer[0] = 0; // Make sure the string is terminated - } - // Release the argument list - va_end(args); - // Return the data from the buffer - return g_Buffer; -} - // ------------------------------------------------------------------------------------------------ CSStr QFmtStr(CSStr str, ...) { @@ -70,11 +17,11 @@ CSStr QFmtStr(CSStr str, ...) va_list args; va_start (args, str); // Write the requested contents - sqlite3_vsnprintf(sizeof(g_Buffer), g_Buffer, str, args); + sqlite3_vsnprintf(GetTempBuffSize(), GetTempBuff(), str, args); // Release the argument list va_end(args); // Return the data from the buffer - return g_Buffer; + return GetTempBuff(); } // ------------------------------------------------------------------------------------------------ @@ -100,453 +47,6 @@ bool IsQueryEmpty(CSStr str) return true; } -// ------------------------------------------------------------------------------------------------ -StackGuard::StackGuard() - : m_VM(_SqVM), m_Top(sq_gettop(m_VM)) -{ - /* ... */ -} - -// ------------------------------------------------------------------------------------------------ -StackGuard::StackGuard(HSQUIRRELVM vm) - : m_VM(vm), m_Top(sq_gettop(vm)) -{ - /* ... */ -} - -// ------------------------------------------------------------------------------------------------ -StackGuard::~StackGuard() -{ - sq_pop(m_VM, sq_gettop(m_VM) - m_Top); -} - -// -------------------------------------------------------------------------------------------- -StackStrF::StackStrF(HSQUIRRELVM vm, SQInteger idx, bool fmt) - : mPtr(nullptr) - , mLen(-1) - , mRes(SQ_OK) - , mObj() - , mVM(vm) -{ - const Int32 top = sq_gettop(vm); - // Reset the converted value object - sq_resetobject(&mObj); - // Was the string or value specified? - if (top <= (idx - 1)) - { - mRes = sq_throwerror(vm, "Missing string or value"); - } - // Do we have enough values to call the format function and are we allowed to? - else if (top > idx && fmt) - { - // Pointer to the generated string - SStr str = nullptr; - // Attempt to generate the specified string format - mRes = sqstd_format(vm, idx, &mLen, &str); - // Did the format succeeded but ended up with a null string pointer? - if (SQ_SUCCEEDED(mRes) && !str) - { - mRes = sq_throwerror(vm, "Unable to generate the string"); - } - else - { - mPtr = const_cast< CSStr >(str); - } - } - // Is the value on the stack an actual string? - else if (sq_gettype(vm, idx) == OT_STRING) - { - // Obtain a reference to the string object - mRes = sq_getstackobj(vm, idx, &mObj); - // Could we retrieve the object from the stack? - if (SQ_SUCCEEDED(mRes)) - { - // Keep a strong reference to the object - sq_addref(vm, &mObj); - // Attempt to retrieve the string value from the stack - mRes = sq_getstring(vm, idx, &mPtr); - } - // Did the retrieval succeeded but ended up with a null string pointer? - if (SQ_SUCCEEDED(mRes) && !mPtr) - { - mRes = sq_throwerror(vm, "Unable to retrieve the string"); - } - } - // We have to try and convert it to string - else - { - // Attempt to convert the value from the stack to a string - mRes = sq_tostring(vm, idx); - // Could we convert the specified value to string? - if (SQ_SUCCEEDED(mRes)) - { - // Obtain a reference to the resulted object - mRes = sq_getstackobj(vm, -1, &mObj); - // Could we retrieve the object from the stack? - if (SQ_SUCCEEDED(mRes)) - { - // Keep a strong reference to the object - sq_addref(vm, &mObj); - // Attempt to obtain the string pointer - mRes = sq_getstring(vm, -1, &mPtr); - } - } - // Pop a value from the stack regardless of the result - sq_pop(vm, 1); - // Did the retrieval succeeded but ended up with a null string pointer? - if (SQ_SUCCEEDED(mRes) && !mPtr) - { - mRes = sq_throwerror(vm, "Unable to retrieve the value"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -StackStrF::~StackStrF() -{ - if (mVM && !sq_isnull(mObj)) - { - sq_release(mVM, &mObj); - } -} - -// ------------------------------------------------------------------------------------------------ -void ConnHnd::Validate() const -{ - // Is the handle valid? - if ((m_Hnd == nullptr) || (m_Hnd->mPtr == nullptr)) - { - STHROWF("Invalid SQLite connection reference"); - } -} - -// ------------------------------------------------------------------------------------------------ -void StmtHnd::Validate() const -{ - // Is the handle valid? - if ((m_Hnd == nullptr) || (m_Hnd->mPtr == nullptr)) - { - STHROWF("Invalid SQLite statement reference"); - } -} - -// ------------------------------------------------------------------------------------------------ -ConnHnd::Handle::~Handle() -{ - // Is there anything to close? - if (!mPtr) - { - return; // Nothing to close - } - // Are we dealing with a memory leak? Technically shouldn't reach this situation! - else if (mRef != 0) - { - // Should we deal with undefined behavior instead? How bad is one connection left open? - _SqMod->LogErr("SQLite connection is still referenced (%s)", mName.c_str()); - } - else - { - // NOTE: Should we call sqlite3_interrupt(...) before closing? - Object env; - Function func; - // Flush remaining queries in the queue and ignore the result - Flush(mQueue.size(), env, func); - // Attempt to close the database - if ((sqlite3_close(mPtr)) != SQLITE_OK) - { - _SqMod->LogErr("Unable to close SQLite connection [%s]", sqlite3_errmsg(mPtr)); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void ConnHnd::Handle::Create(CSStr name, Int32 flags, CSStr vfs) -{ - // Make sure a previous connection doesn't exist - if (mPtr) - { - STHROWF("Unable to connect to database. Database already connected"); - } - // Make sure the name is valid - else if (!name || *name == '\0') - { - STHROWF("Unable to connect to database. The name is invalid"); - } - // Attempt to create the database connection - else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK) - { - // Grab the error message before destroying the handle - String msg(sqlite3_errmsg(mPtr) ? sqlite3_errmsg(mPtr) : _SC("Unknown reason")); - // Must be destroyed regardless of result - sqlite3_close(mPtr); - // Explicitly make sure it's null - mPtr = nullptr; - // Now its safe to throw the error - STHROWF("Unable to connect to database [%s]", msg.c_str()); - } - // Let's save the specified information - mName.assign(name); - mFlags = flags; - mVFS.assign(vfs ? vfs : _SC("")); - // Optional check if database is initially stored in memory - mMemory = (mName.compare(_SC(":memory:")) == 0); -} - -// ------------------------------------------------------------------------------------------------ -Int32 ConnHnd::Handle::Flush(Uint32 num, Object & env, Function & func) -{ - // Do we even have a valid connection? - if (!mPtr) - { - return -1; // No connection! - } - // Is there anything to flush? - else if (!num || mQueue.empty()) - { - return 0; // Nothing to process! - } - // Can we even flush that many? - else if (num > mQueue.size()) - { - num = mQueue.size(); - } - // Generate the function that should be called upon error - Function callback = Function(env.GetVM(), env.GetObject(), func.GetFunc()); - // Obtain iterators to the range of queries that should be flushed - QueryList::iterator itr = mQueue.begin(); - QueryList::iterator end = mQueue.begin() + num; - // Attempt to begin the flush transaction - if ((mStatus = sqlite3_exec(mPtr, "BEGIN", nullptr, nullptr, nullptr)) != SQLITE_OK) - { - STHROWF("Unable to begin flush transaction [%s]", sqlite3_errmsg(mPtr)); - } - // Process all queries within range of selection - for (; itr != end; ++itr) - { - // Should we manually terminate this query? - /* - if (*(*itr).rbegin() != ';') - { - itr->push_back(';'); - } - */ - // Attempt to execute the currently processed query string - if ((mStatus = sqlite3_exec(mPtr, itr->c_str(), nullptr, nullptr, nullptr)) == SQLITE_OK) - { - continue; - } - // Do we have to execute any callback to resolve our issue? - else if (!callback.IsNull()) - { - try - { - // Ask the callback whether the query processing should end here - SharedPtr< bool > ret = callback.Evaluate< bool, Int32, const String & >(mStatus, *itr); - // Should we break here? - if (!!ret && (*ret == false)) - { - break; - } - } - catch (const Sqrat::Exception & e) - { - _SqMod->LogErr("Squirrel error caught in flush handler [%s]", e.Message().c_str()); - } - catch (const std::exception & e) - { - _SqMod->LogErr("Program error caught in flush handler [%s]", e.what()); - } - catch (...) - { - _SqMod->LogErr("Unknown error caught in flush handler"); - } - } - } - // Erase all queries till end or till the point of failure (if any occurred) - mQueue.erase(mQueue.begin(), itr); - // Attempt to commit changes requested during transaction - if ((mStatus = sqlite3_exec(mPtr, "COMMIT", nullptr, nullptr, nullptr)) == SQLITE_OK) - { - return sqlite3_changes(mPtr); - } - // Attempt to roll back erroneous changes - else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", nullptr, nullptr, nullptr)) != SQLITE_OK) - { - STHROWF("Unable to rollback flush transaction [%s]", sqlite3_errmsg(mPtr)); - } - // The transaction failed somehow but we managed to rollback - else - { - STHROWF("Unable to commit flush transaction [%s]", sqlite3_errmsg(mPtr)); - } - // Operation failed - return -1; -} - -// ------------------------------------------------------------------------------------------------ -StmtHnd::Handle::~Handle() -{ - // Is there anything to finalize? - if (!mPtr) - { - return; // Nothing to finalize - } - // Are we dealing with a memory leak? Technically shouldn't reach this situation! - else if (mRef != 0) - { - // Should we deal with undefined behavior instead? How bad is one statement left alive? - _SqMod->LogErr("SQLite statement is still referenced (%s)", mQuery.c_str()); - } - else - { - // Attempt to finalize the statement - if ((sqlite3_finalize(mPtr)) != SQLITE_OK) - { - _SqMod->LogErr("Unable to finalize SQLite statement [%s]", mConn.ErrMsg()); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void StmtHnd::Handle::Create(CSStr query) -{ - // Make sure a previous statement doesn't exist - if (mPtr) - { - STHROWF("Unable to prepare statement. Statement already prepared"); - } - // Is the specified database connection is valid? - else if (!mConn) - { - STHROWF("Unable to prepare statement. Invalid connection handle"); - } - // Save the query string and therefore multiple strlen(...) calls - mQuery.assign(query ? query : _SC("")); - // Is the specified query string we just saved, valid? - if (mQuery.empty()) - { - STHROWF("Unable to prepare statement. Invalid query string"); - } - // Attempt to prepare a statement with the specified query string - else if ((mStatus = sqlite3_prepare_v2(mConn, mQuery.c_str(), (Int32)mQuery.size(), - &mPtr, nullptr)) != SQLITE_OK) - { - // Clear the query string since it failed - mQuery.clear(); - // Explicitly make sure the handle is null - mPtr = nullptr; - // Now it's safe to throw the error - STHROWF("Unable to prepare statement [%s]", mConn.ErrMsg()); - } - else - { - // Obtain the number of available columns - mColumns = sqlite3_column_count(mPtr); - } -} - -// ------------------------------------------------------------------------------------------------ -Int32 StmtHnd::Handle::GetColumnIndex(CSStr name) -{ - // Validate the handle - if (!mPtr) - { - STHROWF("Invalid SQLite statement"); - } - // Are the names cached? - else if (mIndexes.empty()) - { - for (Int32 i = 0; i < mColumns; ++i) - { - // Get the column name at the current index - CSStr name = (CSStr)sqlite3_column_name(mPtr, i); - // Validate the name - if (!name) - { - STHROWF("Unable to retrieve column name for index (%d)", i); - } - // Save it to guarantee the same lifetime as this instance - else - { - mIndexes[name] = i; - } - } - } - // Attempt to find the specified column - const Indexes::iterator itr = mIndexes.find(name); - // Was there a column with the specified name? - if (itr != mIndexes.end()) - { - return itr->second; - } - // No such column exists (expecting the invoker to validate the result) - return -1; -} - -// ------------------------------------------------------------------------------------------------ -Transaction::Transaction(const Connection & db) - : Transaction(db.GetHandle()) -{ - /* ... */ -} - -// ------------------------------------------------------------------------------------------------ -Transaction::Transaction(const ConnHnd & db) - : m_Connection(db), m_Committed(false) -{ - // Was the specified database connection valid? - if (!m_Connection) - { - STHROWF("Invalid connection handle"); - } - // Attempt to begin transaction - else if ((m_Connection = sqlite3_exec(m_Connection, "BEGIN", nullptr, nullptr, nullptr)) != SQLITE_OK) - { - STHROWF("Unable to begin transaction [%s]", m_Connection.ErrMsg()); - } -} - -// ------------------------------------------------------------------------------------------------ -Transaction::~Transaction() -{ - // Was this transaction successfully committed? - if (m_Committed) - { - return; // We're done here! - } - // Attempt to roll back changes because this failed to commit - if ((m_Connection = sqlite3_exec(m_Connection, "ROLLBACK", nullptr, nullptr, nullptr)) != SQLITE_OK) - { - STHROWF("Unable to rollback transaction [%s]", m_Connection.ErrMsg()); - } -} - -// ------------------------------------------------------------------------------------------------ -bool Transaction::Commit() -{ - // We shouldn't even be here if there wasn't a valid connection but let's be sure - if (!m_Connection) - { - STHROWF("Invalid database connection"); - } - // Was this transaction already committed? - else if (m_Committed) - { - STHROWF("Transaction was already committed"); - } - // Attempt to commit the change during this transaction - else if ((m_Connection = sqlite3_exec(m_Connection, "COMMIT", nullptr, nullptr, nullptr)) != SQLITE_OK) - { - STHROWF("Unable to commit transaction [%s]", m_Connection.ErrMsg()); - } - else - { - m_Committed = true; // Everything was committed successfully - } - // Return the result - return m_Committed; -} - // ------------------------------------------------------------------------------------------------ CSStr GetErrStr(Int32 status) { @@ -596,9 +96,9 @@ CSStr EscapeString(CSStr str) return _SC(""); // Default to empty string } // Attempt to escape the specified string - sqlite3_snprintf(sizeof(g_Buffer), g_Buffer, "%q", str); + sqlite3_snprintf(GetTempBuffSize(), GetTempBuff(), "%q", str); // Return the resulted string - return g_Buffer; + return GetTempBuff(); } // ------------------------------------------------------------------------------------------------ @@ -619,11 +119,11 @@ CCStr EscapeStringEx(SQChar spec, CCStr str) // Apply the format specifier fs[1] = spec; // Attempt to escape the specified string - sqlite3_snprintf(sizeof(g_Buffer), g_Buffer, fs, str); + sqlite3_snprintf(GetTempBuffSize(), GetTempBuff(), fs, str); // Restore the format specifier fs[1] = 'q'; // Return the resulted string - return g_Buffer; + return GetTempBuff(); } // ------------------------------------------------------------------------------------------------ @@ -643,7 +143,7 @@ CCStr ArrayToQueryColumns(Array & arr) // Obtain the start of the array std::vector< String >::iterator itr = values.begin(); // Process all elements within range - for (; itr != values.end() && offset < sizeof(g_Buffer); ++itr) + for (; itr != values.end() && offset < GetTempBuffSize(); ++itr) { // Is the name valid? if (itr->empty()) @@ -651,7 +151,7 @@ CCStr ArrayToQueryColumns(Array & arr) STHROWF("Invalid column name"); } // Attempt to append the column name to the buffer - sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", itr->c_str()); + sqlite3_snprintf(GetTempBuffSize() - offset, GetTempBuff() + offset, "[%q], ", itr->c_str()); // Add the column name size to the offset offset += itr->size(); // Also include the comma and space in the offset @@ -660,14 +160,14 @@ CCStr ArrayToQueryColumns(Array & arr) // Trim the last coma and space if (offset >= 2) { - g_Buffer[offset-2] = '\0'; + GetTempBuff()[offset-2] = '\0'; } else { - g_Buffer[0] = '\0'; + GetTempBuff()[0] = '\0'; } // Return the resulted string - return g_Buffer; + return GetTempBuff(); } // ------------------------------------------------------------------------------------------------ @@ -690,7 +190,7 @@ CCStr TableToQueryColumns(Table & tbl) STHROWF("Invalid or empty column name"); } // Attempt to append the column name to the buffer - sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", name.c_str()); + sqlite3_snprintf(GetTempBuffSize() - offset, GetTempBuff() + offset, "[%q], ", name.c_str()); // Add the column name size to the offset offset += name.size(); // Also include the comma and space in the offset @@ -699,14 +199,14 @@ CCStr TableToQueryColumns(Table & tbl) // Trim the last coma and space if (offset >= 2) { - g_Buffer[offset-2] = '\0'; + GetTempBuff()[offset-2] = '\0'; } else { - g_Buffer[0] = '\0'; + GetTempBuff()[0] = '\0'; } // Return the resulted string - return g_Buffer; + return GetTempBuff(); } } // Namespace:: SqMod diff --git a/modules/sqlite/Common.hpp b/modules/sqlite/Common.hpp index 4d81833d..99643b87 100644 --- a/modules/sqlite/Common.hpp +++ b/modules/sqlite/Common.hpp @@ -2,18 +2,10 @@ #define _SQSQLITE_COMMON_HPP_ // ------------------------------------------------------------------------------------------------ -#include "ModBase.hpp" - -// ------------------------------------------------------------------------------------------------ -#include - -// ------------------------------------------------------------------------------------------------ -#include -#include +#include "Base/Utility.hpp" // ------------------------------------------------------------------------------------------------ #include -#include // ------------------------------------------------------------------------------------------------ namespace SqMod { @@ -39,26 +31,6 @@ class Transaction; #define SQSQLITE_VERSION_MINOR 0 #define SQSQLITE_VERSION_PATCH 1 -/* ------------------------------------------------------------------------------------------------ - * Retrieve the temporary buffer. -*/ -SStr GetTempBuff(); - -/* ------------------------------------------------------------------------------------------------ - * Retrieve the size of the temporary buffer. -*/ -Uint32 GetTempBuffSize(); - -/* ------------------------------------------------------------------------------------------------ - * Throw a formatted exception. -*/ -void SqThrowF(CSStr str, ...); - -/* ------------------------------------------------------------------------------------------------ - * Generate a formatted string. -*/ -CSStr FmtStr(CSStr str, ...); - /* ------------------------------------------------------------------------------------------------ * Generate a formatted query. */ @@ -69,949 +41,6 @@ CSStr QFmtStr(CSStr str, ...); */ bool IsQueryEmpty(CSStr str); -/* ------------------------------------------------------------------------------------------------ - * Implements RAII to restore the VM stack to it's initial size on function exit. -*/ -struct StackGuard -{ - /* -------------------------------------------------------------------------------------------- - * Default constructor. - */ - StackGuard(); - - /* -------------------------------------------------------------------------------------------- - * Base constructor. - */ - StackGuard(HSQUIRRELVM vm); - - /* -------------------------------------------------------------------------------------------- - * Destructor. - */ - ~StackGuard(); - -private: - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. - */ - StackGuard(const StackGuard &); - - /* -------------------------------------------------------------------------------------------- - * Move constructor. - */ - StackGuard(StackGuard &&); - - /* -------------------------------------------------------------------------------------------- - * Copy assignment operator. - */ - StackGuard & operator = (const StackGuard &); - - /* -------------------------------------------------------------------------------------------- - * Move assignment operator. - */ - StackGuard & operator = (StackGuard &&); - -private: - - // -------------------------------------------------------------------------------------------- - HSQUIRRELVM m_VM; // The VM where the stack should be restored. - Int32 m_Top; // The top of the stack when this instance was created. -}; - -/* ------------------------------------------------------------------------------------------------ - * Helper structure for retrieving a value from the stack as a string or a formatted string. -*/ -struct StackStrF -{ - // -------------------------------------------------------------------------------------------- - CSStr mPtr; // Pointer to the C string that was retrieved. - SQInteger mLen; // The string length if it could be retrieved. - SQRESULT mRes; // The result of the retrieval attempts. - HSQOBJECT mObj; // Strong reference to the string object. - HSQUIRRELVM mVM; // The associated virtual machine. - - /* -------------------------------------------------------------------------------------------- - * Base constructor. - */ - StackStrF(HSQUIRRELVM vm, SQInteger idx, bool fmt = true); - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. (disabled) - */ - StackStrF(const StackStrF & o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. (disabled) - */ - StackStrF(StackStrF && o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Destructor. - */ - ~StackStrF(); - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. (disabled) - */ - StackStrF & operator = (const StackStrF & o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. (disabled) - */ - StackStrF & operator = (StackStrF && o) = delete; -}; - -/* ------------------------------------------------------------------------------------------------ - * Manages a reference counted database connection handle. -*/ -class ConnHnd -{ - // -------------------------------------------------------------------------------------------- - friend class Connection; - friend class Statement; - -public: - - // -------------------------------------------------------------------------------------------- - typedef sqlite3 Type; // The managed type. - - // -------------------------------------------------------------------------------------------- - typedef Type* Pointer; // Pointer to the managed type. - typedef const Type* ConstPtr; // Constant pointer to the managed type. - - // -------------------------------------------------------------------------------------------- - typedef Type& Reference; // Reference to the managed type. - typedef const Type& ConstRef; // Constant reference to the managed type. - - // -------------------------------------------------------------------------------------------- - typedef unsigned int Counter; // Reference counter type. - - /* -------------------------------------------------------------------------------------------- - * Validate the connection handle and throw an error if invalid. - */ - void Validate() const; - -protected: - - // -------------------------------------------------------------------------------------------- - typedef std::vector< String > QueryList; // Container used to queue queries. - - /* -------------------------------------------------------------------------------------------- - * The structure that holds the data associated with a certain connection. - */ - struct Handle - { - // ---------------------------------------------------------------------------------------- - Pointer mPtr; // The connection handle resource. - Counter mRef; // Reference count to the managed handle. - - // ---------------------------------------------------------------------------------------- - Int32 mStatus; // The last status code of this connection handle. - - // ---------------------------------------------------------------------------------------- - QueryList mQueue; // A queue of queries to be executed in groups. - - // ---------------------------------------------------------------------------------------- - Int32 mFlags; // The flags used to create the database connection handle. - String mName; // The specified name to be used as the database file. - String mVFS; // The specified virtual file system. - - // ---------------------------------------------------------------------------------------- - bool mMemory; // Whether the database exists in memory and not disk. - bool mTrace; // Whether tracing was activated on the database. - bool mProfile; // Whether profiling was activated on the database. - - /* ---------------------------------------------------------------------------------------- - * Base constructor. - */ - Handle(Counter counter) - : mPtr(nullptr) - , mRef(counter) - , mStatus(SQLITE_OK) - , mQueue() - , mFlags(0) - , mName() - , mVFS() - , mMemory(false) - , mTrace(false) - , mProfile(false) - { - /* ... */ - } - - /* ---------------------------------------------------------------------------------------- - * Destructor. - */ - ~Handle(); - - /* ---------------------------------------------------------------------------------------- - * Create the database connection resource. - */ - void Create(CSStr name, Int32 flags, CSStr vfs); - - /* ---------------------------------------------------------------------------------------- - * Execute a specific amount of queries from the queue. - */ - Int32 Flush(Uint32 num, Object & env, Function & func); - }; - -private: - - // -------------------------------------------------------------------------------------------- - Handle* m_Hnd; - - /* -------------------------------------------------------------------------------------------- - * Grab a strong reference to a connection handle. - */ - void Grab() - { - if (m_Hnd) - { - ++(m_Hnd->mRef); - } - } - - /* -------------------------------------------------------------------------------------------- - * Drop a strong reference to a connection handle. - */ - void Drop() - { - if (m_Hnd && --(m_Hnd->mRef) == 0) - { - delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) - } - } - - /* -------------------------------------------------------------------------------------------- - * Base constructor. - */ - ConnHnd(CSStr name) - : m_Hnd(name ? new Handle(1) : nullptr) - { - /* ... */ - } - -public: - - /* -------------------------------------------------------------------------------------------- - * Default constructor (null). - */ - ConnHnd() - : m_Hnd(nullptr) - { - /* ... */ - } - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. - */ - ConnHnd(const ConnHnd & o) - : m_Hnd(o.m_Hnd) - { - Grab(); - } - - /* -------------------------------------------------------------------------------------------- - * Move constructor. - */ - ConnHnd(ConnHnd && o) - : m_Hnd(o.m_Hnd) - { - o.m_Hnd = nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Destructor. - */ - ~ConnHnd() - { - Drop(); - } - - /* -------------------------------------------------------------------------------------------- - * Copy assignment operator. - */ - ConnHnd & operator = (const ConnHnd & o) - { - if (m_Hnd != o.m_Hnd) - { - Drop(); - m_Hnd = o.m_Hnd; - Grab(); - } - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Move assignment operator. - */ - ConnHnd & operator = (ConnHnd && o) - { - if (m_Hnd != o.m_Hnd) - { - m_Hnd = o.m_Hnd; - o.m_Hnd = nullptr; - } - - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Status assignment operator. - */ - ConnHnd & operator = (Int32 status) - { - if (m_Hnd) - { - m_Hnd->mStatus = status; - } - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Perform an equality comparison between two connection handles. - */ - bool operator == (const ConnHnd & o) const - { - return (m_Hnd == o.m_Hnd); - } - - /* -------------------------------------------------------------------------------------------- - * Perform an inequality comparison between two connection handles. - */ - bool operator != (const ConnHnd & o) const - { - return (m_Hnd != o.m_Hnd); - } - - /* -------------------------------------------------------------------------------------------- - * Perform an equality comparison with an integer value status. - */ - bool operator == (Int32 status) const - { - if (m_Hnd) - { - return (m_Hnd->mStatus == status); - } - return false; - } - - /* -------------------------------------------------------------------------------------------- - * Perform an inequality comparison with an integer value status. - */ - bool operator != (Int32 status) const - { - if (m_Hnd) - { - return (m_Hnd->mStatus != status); - } - return false; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to boolean for use in boolean operations. - */ - operator bool () const - { - return m_Hnd && m_Hnd->mPtr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Pointer () - { - return m_Hnd ? m_Hnd->mPtr : nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Pointer () const - { - return m_Hnd ? m_Hnd->mPtr : nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Reference () - { - assert(m_Hnd && m_Hnd->mPtr); - return *(m_Hnd->mPtr); - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator ConstRef () const - { - assert(m_Hnd && m_Hnd->mPtr); - return *(m_Hnd->mPtr); - } - - /* -------------------------------------------------------------------------------------------- - * Member operator for dereferencing the managed pointer. - */ - Handle * operator -> () const - { - assert(m_Hnd); - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Indirection operator for obtaining a reference of the managed pointer. - */ - Handle & operator * () const - { - assert(m_Hnd); - return *m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the raw handle structure pointer. - */ - Handle * HndPtr() - { - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the raw handle structure pointer. - */ - Handle * HndPtr() const - { - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the number of active references to the managed instance. - */ - Counter Count() const - { - return m_Hnd ? m_Hnd->mRef : 0; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the last known status code. - */ - Int32 Status() const - { - return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the message of the last received error code. - */ - CCStr ErrStr() const - { - // SQLite does it's null pointer validations internally - if (m_Hnd) - { - return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr)); - } - return _SC(""); - } - - /* -------------------------------------------------------------------------------------------- - * Return the last error message associated with this database connection. - */ - CCStr ErrMsg() const - { - // SQLite does it's null pointer validations internally - if (m_Hnd) - { - return sqlite3_errmsg(m_Hnd->mPtr); - } - return _SC(""); - } - - /* -------------------------------------------------------------------------------------------- - * Return the numeric result code for the most recent failed API call (if any). - */ - Int32 ErrNo() const - { - // SQLite does it's null pointer validations internally - if (m_Hnd) - { - return sqlite3_errcode(m_Hnd->mPtr); - } - return SQLITE_NOMEM; - } - - /* -------------------------------------------------------------------------------------------- - * Return the extended numeric result code for the most recent failed API call (if any). - */ - Int32 ExErrNo() const - { - // SQLite does it's null pointer validations internally - if (m_Hnd) - { - return sqlite3_extended_errcode(m_Hnd->mPtr); - } - return SQLITE_NOMEM; - } -}; - -/* ------------------------------------------------------------------------------------------------ - * Manages a reference counted database statement handle. -*/ -class StmtHnd -{ - // -------------------------------------------------------------------------------------------- - friend class Connection; - friend class Statement; - -public: - - // -------------------------------------------------------------------------------------------- - typedef sqlite3_stmt Type; // The managed type. - - // -------------------------------------------------------------------------------------------- - typedef Type* Pointer; // Pointer to the managed type. - typedef const Type* ConstPtr; // Constant pointer to the managed type. - - // -------------------------------------------------------------------------------------------- - typedef Type& Reference; // Reference to the managed type. - typedef const Type& ConstRef; // Constant reference to the managed type. - - // -------------------------------------------------------------------------------------------- - typedef unsigned int Counter; // Reference counter type. - - /* -------------------------------------------------------------------------------------------- - * Validate the statement handle and throw an error if invalid. - */ - void Validate() const; - -protected: - - // -------------------------------------------------------------------------------------------- - typedef std::map< String, int > Indexes; // Container used to identify column indexes. - - /* -------------------------------------------------------------------------------------------- - * The structure that holds the data associated with a certain statement. - */ - struct Handle - { - // ---------------------------------------------------------------------------------------- - Pointer mPtr; // The statement handle resource. - Counter mRef; // Reference count to the managed handle. - - // ---------------------------------------------------------------------------------------- - Int32 mStatus; // The last status code of this connection handle. - - // ---------------------------------------------------------------------------------------- - ConnHnd mConn; // The handle to the associated database connection. - - // ---------------------------------------------------------------------------------------- - String mQuery; // The query string used to create this statement. - - // ---------------------------------------------------------------------------------------- - Int32 mColumns; // The amount of columns available in this statement. - Indexes mIndexes; // An associative container with column names and their index. - - // ---------------------------------------------------------------------------------------- - bool mGood; // True when a row has been fetched with step. - bool mDone; // True when the last step had no more rows to fetch. - - /* ---------------------------------------------------------------------------------------- - * Base constructor. - */ - Handle(const ConnHnd & conn, Counter counter) - : mPtr(nullptr) - , mRef(counter) - , mStatus(SQLITE_OK) - , mConn(conn) - , mQuery() - , mColumns(0) - , mIndexes() - , mGood(false) - , mDone(false) - { - /* ... */ - } - - /* ---------------------------------------------------------------------------------------- - * Destructor. - */ - ~Handle(); - - /* ---------------------------------------------------------------------------------------- - * Create the database statement resource. - */ - void Create(CSStr query); - - /* ---------------------------------------------------------------------------------------- - * Check whether a specific index is in range. - */ - bool CheckIndex(Int32 idx) const - { - return (idx >= 0) && (idx <= mColumns); - } - - /* ---------------------------------------------------------------------------------------- - * Retrieve the column index associated with the specified name. - */ - Int32 GetColumnIndex(CSStr name); - }; - -private: - - // -------------------------------------------------------------------------------------------- - Handle* m_Hnd; - - /* -------------------------------------------------------------------------------------------- - * Grab a strong reference to a statement handle. - */ - void Grab() - { - if (m_Hnd) - { - ++(m_Hnd->mRef); - } - } - - /* -------------------------------------------------------------------------------------------- - * Drop a strong reference to a statement handle. - */ - void Drop() - { - if (m_Hnd && --(m_Hnd->mRef) == 0) - { - delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) - } - } - - /* -------------------------------------------------------------------------------------------- - * Base constructor. - */ - StmtHnd(const ConnHnd & db) - : m_Hnd(new Handle(db, 1)) - { - /* ... */ - } - -public: - - /* -------------------------------------------------------------------------------------------- - * Default constructor (null). - */ - StmtHnd() - : m_Hnd(nullptr) - { - /* ... */ - } - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. - */ - StmtHnd(const StmtHnd & o) - : m_Hnd(o.m_Hnd) - - { - Grab(); - } - - /* -------------------------------------------------------------------------------------------- - * Move constructor. - */ - StmtHnd(StmtHnd && o) - : m_Hnd(o.m_Hnd) - { - o.m_Hnd = nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Destructor. - */ - ~StmtHnd() - { - Drop(); - } - - /* -------------------------------------------------------------------------------------------- - * Copy assignment operator. - */ - StmtHnd & operator = (const StmtHnd & o) - { - if (m_Hnd != o.m_Hnd) - { - Drop(); - m_Hnd = o.m_Hnd; - Grab(); - } - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Move assignment operator. - */ - StmtHnd & operator = (StmtHnd && o) - { - if (m_Hnd != o.m_Hnd) - { - m_Hnd = o.m_Hnd; - o.m_Hnd = nullptr; - } - - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Status assignment operator. - */ - StmtHnd & operator = (Int32 status) - { - if (m_Hnd) - { - m_Hnd->mStatus = status; - } - return *this; - } - - /* -------------------------------------------------------------------------------------------- - * Perform an equality comparison between two statement handles. - */ - bool operator == (const StmtHnd & o) const - { - return (m_Hnd == o.m_Hnd); - } - - /* -------------------------------------------------------------------------------------------- - * Perform an inequality comparison between two statement handles. - */ - bool operator != (const StmtHnd & o) const - { - return (m_Hnd != o.m_Hnd); - } - - /* -------------------------------------------------------------------------------------------- - * Perform an equality comparison with an integer value status. - */ - bool operator == (Int32 status) const - { - if (m_Hnd) - { - return (m_Hnd->mStatus == status); - } - return false; - } - - /* -------------------------------------------------------------------------------------------- - * Perform an inequality comparison with an integer status value. - */ - bool operator != (Int32 status) const - { - if (m_Hnd) - { - return (m_Hnd->mStatus != status); - } - return false; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to boolean for use in boolean operations. - */ - operator bool () const - { - return m_Hnd && m_Hnd->mPtr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Pointer () - { - return m_Hnd ? m_Hnd->mPtr : nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Pointer () const - { - return m_Hnd ? m_Hnd->mPtr : nullptr; - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator Reference () - { - assert(m_Hnd && m_Hnd->mPtr); - return *(m_Hnd->mPtr); - } - - /* -------------------------------------------------------------------------------------------- - * Implicit conversion to the managed instance. - */ - operator ConstRef () const - { - assert(m_Hnd && m_Hnd->mPtr); - return *(m_Hnd->mPtr); - } - - /* -------------------------------------------------------------------------------------------- - * Member operator for dereferencing the managed pointer. - */ - Handle * operator -> () const - { - assert(m_Hnd); - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Indirection operator for obtaining a reference of the managed pointer. - */ - Handle & operator * () const - { - assert(m_Hnd); - return *m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the raw handle structure pointer. - */ - Handle * HndPtr() - { - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the raw handle structure pointer. - */ - Handle * HndPtr() const - { - return m_Hnd; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the number of active references to the managed instance. - */ - Counter Count() const - { - return m_Hnd ? m_Hnd->mRef : 0; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the last known status code. - */ - Int32 Status() const - { - return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the message of the last received error code. - */ - CCStr ErrStr() const - { - if (m_Hnd) - { - return m_Hnd->mConn.ErrStr(); - } - return _SC(""); - } - - /* -------------------------------------------------------------------------------------------- - * Return the last error message associated with this database connection. - */ - CCStr ErrMsg() const - { - if (m_Hnd) - { - return m_Hnd->mConn.ErrMsg(); - } - return _SC(""); - } - - /* -------------------------------------------------------------------------------------------- - * Return the numeric result code for the most recent failed API call (if any). - */ - Int32 ErrNo() const - { - if (m_Hnd) - { - return m_Hnd->mConn.ErrNo(); - } - return SQLITE_NOMEM; - } - - /* -------------------------------------------------------------------------------------------- - * Return the extended numeric result code for the most recent failed API call (if any). - */ - Int32 ExErrNo() const - { - if (m_Hnd) - { - return m_Hnd->mConn.ExErrNo(); - } - return SQLITE_NOMEM; - } -}; - -/* ------------------------------------------------------------------------------------------------ - * Implements the RAII pattern for database transactions. -*/ -class Transaction -{ -public: - - /* -------------------------------------------------------------------------------------------- - * Construct by taking the handle from a connection. - */ - Transaction(const Connection & db); - - /* -------------------------------------------------------------------------------------------- - * Construct using the direct connection handle. - */ - Transaction(const ConnHnd & db); - - /* -------------------------------------------------------------------------------------------- - * Copy constructor. (disabled) - */ - Transaction(const Transaction & o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Move constructor. (disabled) - */ - Transaction(Transaction && o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Destructor. - */ - ~Transaction(); - - /* -------------------------------------------------------------------------------------------- - * Copy assignment operator. (disabled) - */ - Transaction & operator = (const Transaction & o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Move assignment operator. (disabled) - */ - Transaction & operator = (Transaction && o) = delete; - - /* -------------------------------------------------------------------------------------------- - * Attempt to commit changes to the database. - */ - bool Commit(); - - /* -------------------------------------------------------------------------------------------- - * See whether the change during this transaction were successfully committed. - */ - bool Commited() const - { - return m_Committed; - } - -private: - - // -------------------------------------------------------------------------------------------- - ConnHnd m_Connection; // The database connection handle where the transaction began. - bool m_Committed; // Whether changes were successfully committed to the database. -}; - /* ------------------------------------------------------------------------------------------------ * Retrieve the string representation of a certain status code. */ diff --git a/modules/sqlite/Connection.cpp b/modules/sqlite/Connection.cpp index 2b333937..5625d1aa 100644 --- a/modules/sqlite/Connection.cpp +++ b/modules/sqlite/Connection.cpp @@ -1,10 +1,6 @@ // ------------------------------------------------------------------------------------------------ #include "Connection.hpp" #include "Statement.hpp" -#include "Module.hpp" - -// ------------------------------------------------------------------------------------------------ -#include // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Connection.hpp b/modules/sqlite/Connection.hpp index 9dc3d088..51b440de 100644 --- a/modules/sqlite/Connection.hpp +++ b/modules/sqlite/Connection.hpp @@ -2,8 +2,7 @@ #define _SQSQLITE_CONNECTION_HPP_ // ------------------------------------------------------------------------------------------------ -#include "Common.hpp" -#include "SqAPI.h" +#include "Handle/Connection.hpp" // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Handle/Connection.cpp b/modules/sqlite/Handle/Connection.cpp new file mode 100644 index 00000000..aaac893c --- /dev/null +++ b/modules/sqlite/Handle/Connection.cpp @@ -0,0 +1,170 @@ +// ------------------------------------------------------------------------------------------------ +#include "Handle/Connection.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +void ConnHnd::Validate() const +{ + // Is the handle valid? + if ((m_Hnd == nullptr) || (m_Hnd->mPtr == nullptr)) + { + STHROWF("Invalid SQLite connection reference"); + } +} + +// ------------------------------------------------------------------------------------------------ +ConnHnd::Handle::~Handle() +{ + // Is there anything to close? + if (!mPtr) + { + return; // Nothing to close + } + // Are we dealing with a memory leak? Technically shouldn't reach this situation! + else if (mRef != 0) + { + // Should we deal with undefined behavior instead? How bad is one connection left open? + _SqMod->LogErr("SQLite connection is still referenced (%s)", mName.c_str()); + } + else + { + // NOTE: Should we call sqlite3_interrupt(...) before closing? + Object env; + Function func; + // Flush remaining queries in the queue and ignore the result + Flush(mQueue.size(), env, func); + // Attempt to close the database + if ((sqlite3_close(mPtr)) != SQLITE_OK) + { + _SqMod->LogErr("Unable to close SQLite connection [%s]", sqlite3_errmsg(mPtr)); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ConnHnd::Handle::Create(CSStr name, Int32 flags, CSStr vfs) +{ + // Make sure a previous connection doesn't exist + if (mPtr) + { + STHROWF("Unable to connect to database. Database already connected"); + } + // Make sure the name is valid + else if (!name || *name == '\0') + { + STHROWF("Unable to connect to database. The name is invalid"); + } + // Attempt to create the database connection + else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK) + { + // Grab the error message before destroying the handle + String msg(sqlite3_errmsg(mPtr) ? sqlite3_errmsg(mPtr) : _SC("Unknown reason")); + // Must be destroyed regardless of result + sqlite3_close(mPtr); + // Explicitly make sure it's null + mPtr = nullptr; + // Now its safe to throw the error + STHROWF("Unable to connect to database [%s]", msg.c_str()); + } + // Let's save the specified information + mName.assign(name); + mFlags = flags; + mVFS.assign(vfs ? vfs : _SC("")); + // Optional check if database is initially stored in memory + mMemory = (mName.compare(_SC(":memory:")) == 0); +} + +// ------------------------------------------------------------------------------------------------ +Int32 ConnHnd::Handle::Flush(Uint32 num, Object & env, Function & func) +{ + // Do we even have a valid connection? + if (!mPtr) + { + return -1; // No connection! + } + // Is there anything to flush? + else if (!num || mQueue.empty()) + { + return 0; // Nothing to process! + } + // Can we even flush that many? + else if (num > mQueue.size()) + { + num = mQueue.size(); + } + // Generate the function that should be called upon error + Function callback = Function(env.GetVM(), env.GetObject(), func.GetFunc()); + // Obtain iterators to the range of queries that should be flushed + QueryList::iterator itr = mQueue.begin(); + QueryList::iterator end = mQueue.begin() + num; + // Attempt to begin the flush transaction + if ((mStatus = sqlite3_exec(mPtr, "BEGIN", nullptr, nullptr, nullptr)) != SQLITE_OK) + { + STHROWF("Unable to begin flush transaction [%s]", sqlite3_errmsg(mPtr)); + } + // Process all queries within range of selection + for (; itr != end; ++itr) + { + // Should we manually terminate this query? + /* + if (*(*itr).rbegin() != ';') + { + itr->push_back(';'); + } + */ + // Attempt to execute the currently processed query string + if ((mStatus = sqlite3_exec(mPtr, itr->c_str(), nullptr, nullptr, nullptr)) == SQLITE_OK) + { + continue; + } + // Do we have to execute any callback to resolve our issue? + else if (!callback.IsNull()) + { + try + { + // Ask the callback whether the query processing should end here + SharedPtr< bool > ret = callback.Evaluate< bool, Int32, const String & >(mStatus, *itr); + // Should we break here? + if (!!ret && (*ret == false)) + { + break; + } + } + catch (const Sqrat::Exception & e) + { + _SqMod->LogErr("Squirrel error caught in flush handler [%s]", e.Message().c_str()); + } + catch (const std::exception & e) + { + _SqMod->LogErr("Program error caught in flush handler [%s]", e.what()); + } + catch (...) + { + _SqMod->LogErr("Unknown error caught in flush handler"); + } + } + } + // Erase all queries till end or till the point of failure (if any occurred) + mQueue.erase(mQueue.begin(), itr); + // Attempt to commit changes requested during transaction + if ((mStatus = sqlite3_exec(mPtr, "COMMIT", nullptr, nullptr, nullptr)) == SQLITE_OK) + { + return sqlite3_changes(mPtr); + } + // Attempt to roll back erroneous changes + else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", nullptr, nullptr, nullptr)) != SQLITE_OK) + { + STHROWF("Unable to rollback flush transaction [%s]", sqlite3_errmsg(mPtr)); + } + // The transaction failed somehow but we managed to rollback + else + { + STHROWF("Unable to commit flush transaction [%s]", sqlite3_errmsg(mPtr)); + } + // Operation failed + return -1; +} + +} // Namespace:: SqMod \ No newline at end of file diff --git a/modules/sqlite/Handle/Connection.hpp b/modules/sqlite/Handle/Connection.hpp new file mode 100644 index 00000000..6a7f6fe4 --- /dev/null +++ b/modules/sqlite/Handle/Connection.hpp @@ -0,0 +1,407 @@ +#ifndef _SQSQLITE_HANDLE_CONNECTION_HPP_ +#define _SQSQLITE_HANDLE_CONNECTION_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Manages a reference counted database connection handle. +*/ +class ConnHnd +{ + // -------------------------------------------------------------------------------------------- + friend class Connection; + friend class Statement; + +public: + + // -------------------------------------------------------------------------------------------- + typedef sqlite3 Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef unsigned int Counter; // Reference counter type. + + /* -------------------------------------------------------------------------------------------- + * Validate the connection handle and throw an error if invalid. + */ + void Validate() const; + +protected: + + // -------------------------------------------------------------------------------------------- + typedef std::vector< String > QueryList; // Container used to queue queries. + + /* -------------------------------------------------------------------------------------------- + * The structure that holds the data associated with a certain connection. + */ + struct Handle + { + // ---------------------------------------------------------------------------------------- + Pointer mPtr; // The connection handle resource. + Counter mRef; // Reference count to the managed handle. + + // ---------------------------------------------------------------------------------------- + Int32 mStatus; // The last status code of this connection handle. + + // ---------------------------------------------------------------------------------------- + QueryList mQueue; // A queue of queries to be executed in groups. + + // ---------------------------------------------------------------------------------------- + Int32 mFlags; // The flags used to create the database connection handle. + String mName; // The specified name to be used as the database file. + String mVFS; // The specified virtual file system. + + // ---------------------------------------------------------------------------------------- + bool mMemory; // Whether the database exists in memory and not disk. + bool mTrace; // Whether tracing was activated on the database. + bool mProfile; // Whether profiling was activated on the database. + + /* ---------------------------------------------------------------------------------------- + * Base constructor. + */ + Handle(Counter counter) + : mPtr(nullptr) + , mRef(counter) + , mStatus(SQLITE_OK) + , mQueue() + , mFlags(0) + , mName() + , mVFS() + , mMemory(false) + , mTrace(false) + , mProfile(false) + { + /* ... */ + } + + /* ---------------------------------------------------------------------------------------- + * Destructor. + */ + ~Handle(); + + /* ---------------------------------------------------------------------------------------- + * Create the database connection resource. + */ + void Create(CSStr name, Int32 flags, CSStr vfs); + + /* ---------------------------------------------------------------------------------------- + * Execute a specific amount of queries from the queue. + */ + Int32 Flush(Uint32 num, Object & env, Function & func); + }; + +private: + + // -------------------------------------------------------------------------------------------- + Handle * m_Hnd; // The managed handle instance. + + /* -------------------------------------------------------------------------------------------- + * Grab a strong reference to a connection handle. + */ + void Grab() + { + if (m_Hnd) + { + ++(m_Hnd->mRef); + } + } + + /* -------------------------------------------------------------------------------------------- + * Drop a strong reference to a connection handle. + */ + void Drop() + { + if (m_Hnd && --(m_Hnd->mRef) == 0) + { + delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) + } + } + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + ConnHnd(CSStr name) + : m_Hnd(name ? new Handle(1) : nullptr) + { + /* ... */ + } + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor (null). + */ + ConnHnd() + : m_Hnd(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + ConnHnd(const ConnHnd & o) + : m_Hnd(o.m_Hnd) + { + Grab(); + } + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + ConnHnd(ConnHnd && o) + : m_Hnd(o.m_Hnd) + { + o.m_Hnd = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~ConnHnd() + { + Drop(); + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + ConnHnd & operator = (const ConnHnd & o) + { + if (m_Hnd != o.m_Hnd) + { + Drop(); + m_Hnd = o.m_Hnd; + Grab(); + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + ConnHnd & operator = (ConnHnd && o) + { + if (m_Hnd != o.m_Hnd) + { + m_Hnd = o.m_Hnd; + o.m_Hnd = nullptr; + } + + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Status assignment operator. + */ + ConnHnd & operator = (Int32 status) + { + if (m_Hnd) + { + m_Hnd->mStatus = status; + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison between two connection handles. + */ + bool operator == (const ConnHnd & o) const + { + return (m_Hnd == o.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison between two connection handles. + */ + bool operator != (const ConnHnd & o) const + { + return (m_Hnd != o.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison with an integer value status. + */ + bool operator == (Int32 status) const + { + if (m_Hnd) + { + return (m_Hnd->mStatus == status); + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison with an integer value status. + */ + bool operator != (Int32 status) const + { + if (m_Hnd) + { + return (m_Hnd->mStatus != status); + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to boolean for use in boolean operations. + */ + operator bool () const + { + return m_Hnd && m_Hnd->mPtr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () + { + return m_Hnd ? m_Hnd->mPtr : nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () const + { + return m_Hnd ? m_Hnd->mPtr : nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Reference () + { + assert(m_Hnd && m_Hnd->mPtr); + return *(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator ConstRef () const + { + assert(m_Hnd && m_Hnd->mPtr); + return *(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Member operator for dereferencing the managed pointer. + */ + Handle * operator -> () const + { + assert(m_Hnd); + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Indirection operator for obtaining a reference of the managed pointer. + */ + Handle & operator * () const + { + assert(m_Hnd); + return *m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw handle structure pointer. + */ + Handle * HndPtr() + { + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw handle structure pointer. + */ + Handle * HndPtr() const + { + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of active references to the managed instance. + */ + Counter Count() const + { + return m_Hnd ? m_Hnd->mRef : 0; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the last known status code. + */ + Int32 Status() const + { + return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CCStr ErrStr() const + { + // SQLite does it's null pointer validations internally + if (m_Hnd) + { + return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr)); + } + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CCStr ErrMsg() const + { + // SQLite does it's null pointer validations internally + if (m_Hnd) + { + return sqlite3_errmsg(m_Hnd->mPtr); + } + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 ErrNo() const + { + // SQLite does it's null pointer validations internally + if (m_Hnd) + { + return sqlite3_errcode(m_Hnd->mPtr); + } + return SQLITE_NOMEM; + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 ExErrNo() const + { + // SQLite does it's null pointer validations internally + if (m_Hnd) + { + return sqlite3_extended_errcode(m_Hnd->mPtr); + } + return SQLITE_NOMEM; + } +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_HANDLE_CONNECTION_HPP_ diff --git a/modules/sqlite/Handle/Statement.cpp b/modules/sqlite/Handle/Statement.cpp new file mode 100644 index 00000000..0f427a64 --- /dev/null +++ b/modules/sqlite/Handle/Statement.cpp @@ -0,0 +1,117 @@ +// ------------------------------------------------------------------------------------------------ +#include "Handle/Statement.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +void StmtHnd::Validate() const +{ + // Is the handle valid? + if ((m_Hnd == nullptr) || (m_Hnd->mPtr == nullptr)) + { + STHROWF("Invalid SQLite statement reference"); + } +} + +// ------------------------------------------------------------------------------------------------ +StmtHnd::Handle::~Handle() +{ + // Is there anything to finalize? + if (!mPtr) + { + return; // Nothing to finalize + } + // Are we dealing with a memory leak? Technically shouldn't reach this situation! + else if (mRef != 0) + { + // Should we deal with undefined behavior instead? How bad is one statement left alive? + _SqMod->LogErr("SQLite statement is still referenced (%s)", mQuery.c_str()); + } + else + { + // Attempt to finalize the statement + if ((sqlite3_finalize(mPtr)) != SQLITE_OK) + { + _SqMod->LogErr("Unable to finalize SQLite statement [%s]", mConn.ErrMsg()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void StmtHnd::Handle::Create(CSStr query) +{ + // Make sure a previous statement doesn't exist + if (mPtr) + { + STHROWF("Unable to prepare statement. Statement already prepared"); + } + // Is the specified database connection is valid? + else if (!mConn) + { + STHROWF("Unable to prepare statement. Invalid connection handle"); + } + // Save the query string and therefore multiple strlen(...) calls + mQuery.assign(query ? query : _SC("")); + // Is the specified query string we just saved, valid? + if (mQuery.empty()) + { + STHROWF("Unable to prepare statement. Invalid query string"); + } + // Attempt to prepare a statement with the specified query string + else if ((mStatus = sqlite3_prepare_v2(mConn, mQuery.c_str(), (Int32)mQuery.size(), + &mPtr, nullptr)) != SQLITE_OK) + { + // Clear the query string since it failed + mQuery.clear(); + // Explicitly make sure the handle is null + mPtr = nullptr; + // Now it's safe to throw the error + STHROWF("Unable to prepare statement [%s]", mConn.ErrMsg()); + } + else + { + // Obtain the number of available columns + mColumns = sqlite3_column_count(mPtr); + } +} + +// ------------------------------------------------------------------------------------------------ +Int32 StmtHnd::Handle::GetColumnIndex(CSStr name) +{ + // Validate the handle + if (!mPtr) + { + STHROWF("Invalid SQLite statement"); + } + // Are the names cached? + else if (mIndexes.empty()) + { + for (Int32 i = 0; i < mColumns; ++i) + { + // Get the column name at the current index + CSStr name = (CSStr)sqlite3_column_name(mPtr, i); + // Validate the name + if (!name) + { + STHROWF("Unable to retrieve column name for index (%d)", i); + } + // Save it to guarantee the same lifetime as this instance + else + { + mIndexes[name] = i; + } + } + } + // Attempt to find the specified column + const Indexes::iterator itr = mIndexes.find(name); + // Was there a column with the specified name? + if (itr != mIndexes.end()) + { + return itr->second; + } + // No such column exists (expecting the invoker to validate the result) + return -1; +} + +} // Namespace:: SqMod \ No newline at end of file diff --git a/modules/sqlite/Handle/Statement.hpp b/modules/sqlite/Handle/Statement.hpp new file mode 100644 index 00000000..a221c271 --- /dev/null +++ b/modules/sqlite/Handle/Statement.hpp @@ -0,0 +1,412 @@ +#ifndef _SQSQLITE_HANDLE_STATEMENT_HPP_ +#define _SQSQLITE_HANDLE_STATEMENT_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Handle/Connection.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Manages a reference counted database statement handle. +*/ +class StmtHnd +{ + // -------------------------------------------------------------------------------------------- + friend class Connection; + friend class Statement; + +public: + + // -------------------------------------------------------------------------------------------- + typedef sqlite3_stmt Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef unsigned int Counter; // Reference counter type. + + /* -------------------------------------------------------------------------------------------- + * Validate the statement handle and throw an error if invalid. + */ + void Validate() const; + +protected: + + // -------------------------------------------------------------------------------------------- + typedef std::map< String, int > Indexes; // Container used to identify column indexes. + + /* -------------------------------------------------------------------------------------------- + * The structure that holds the data associated with a certain statement. + */ + struct Handle + { + // ---------------------------------------------------------------------------------------- + Pointer mPtr; // The statement handle resource. + Counter mRef; // Reference count to the managed handle. + + // ---------------------------------------------------------------------------------------- + Int32 mStatus; // The last status code of this connection handle. + + // ---------------------------------------------------------------------------------------- + ConnHnd mConn; // The handle to the associated database connection. + + // ---------------------------------------------------------------------------------------- + String mQuery; // The query string used to create this statement. + + // ---------------------------------------------------------------------------------------- + Int32 mColumns; // The amount of columns available in this statement. + Indexes mIndexes; // An associative container with column names and their index. + + // ---------------------------------------------------------------------------------------- + bool mGood; // True when a row has been fetched with step. + bool mDone; // True when the last step had no more rows to fetch. + + /* ---------------------------------------------------------------------------------------- + * Base constructor. + */ + Handle(const ConnHnd & conn, Counter counter) + : mPtr(nullptr) + , mRef(counter) + , mStatus(SQLITE_OK) + , mConn(conn) + , mQuery() + , mColumns(0) + , mIndexes() + , mGood(false) + , mDone(false) + { + /* ... */ + } + + /* ---------------------------------------------------------------------------------------- + * Destructor. + */ + ~Handle(); + + /* ---------------------------------------------------------------------------------------- + * Create the database statement resource. + */ + void Create(CSStr query); + + /* ---------------------------------------------------------------------------------------- + * Check whether a specific index is in range. + */ + bool CheckIndex(Int32 idx) const + { + return (idx >= 0) && (idx <= mColumns); + } + + /* ---------------------------------------------------------------------------------------- + * Retrieve the column index associated with the specified name. + */ + Int32 GetColumnIndex(CSStr name); + }; + +private: + + // -------------------------------------------------------------------------------------------- + Handle * m_Hnd; // The managed handle instance. + + /* -------------------------------------------------------------------------------------------- + * Grab a strong reference to a statement handle. + */ + void Grab() + { + if (m_Hnd) + { + ++(m_Hnd->mRef); + } + } + + /* -------------------------------------------------------------------------------------------- + * Drop a strong reference to a statement handle. + */ + void Drop() + { + if (m_Hnd && --(m_Hnd->mRef) == 0) + { + delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) + } + } + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + StmtHnd(const ConnHnd & db) + : m_Hnd(new Handle(db, 1)) + { + /* ... */ + } + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor (null). + */ + StmtHnd() + : m_Hnd(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + StmtHnd(const StmtHnd & o) + : m_Hnd(o.m_Hnd) + + { + Grab(); + } + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + StmtHnd(StmtHnd && o) + : m_Hnd(o.m_Hnd) + { + o.m_Hnd = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~StmtHnd() + { + Drop(); + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + StmtHnd & operator = (const StmtHnd & o) + { + if (m_Hnd != o.m_Hnd) + { + Drop(); + m_Hnd = o.m_Hnd; + Grab(); + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + StmtHnd & operator = (StmtHnd && o) + { + if (m_Hnd != o.m_Hnd) + { + m_Hnd = o.m_Hnd; + o.m_Hnd = nullptr; + } + + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Status assignment operator. + */ + StmtHnd & operator = (Int32 status) + { + if (m_Hnd) + { + m_Hnd->mStatus = status; + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison between two statement handles. + */ + bool operator == (const StmtHnd & o) const + { + return (m_Hnd == o.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison between two statement handles. + */ + bool operator != (const StmtHnd & o) const + { + return (m_Hnd != o.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison with an integer value status. + */ + bool operator == (Int32 status) const + { + if (m_Hnd) + { + return (m_Hnd->mStatus == status); + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison with an integer status value. + */ + bool operator != (Int32 status) const + { + if (m_Hnd) + { + return (m_Hnd->mStatus != status); + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to boolean for use in boolean operations. + */ + operator bool () const + { + return m_Hnd && m_Hnd->mPtr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () + { + return m_Hnd ? m_Hnd->mPtr : nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () const + { + return m_Hnd ? m_Hnd->mPtr : nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Reference () + { + assert(m_Hnd && m_Hnd->mPtr); + return *(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator ConstRef () const + { + assert(m_Hnd && m_Hnd->mPtr); + return *(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Member operator for dereferencing the managed pointer. + */ + Handle * operator -> () const + { + assert(m_Hnd); + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Indirection operator for obtaining a reference of the managed pointer. + */ + Handle & operator * () const + { + assert(m_Hnd); + return *m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw handle structure pointer. + */ + Handle * HndPtr() + { + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw handle structure pointer. + */ + Handle * HndPtr() const + { + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of active references to the managed instance. + */ + Counter Count() const + { + return m_Hnd ? m_Hnd->mRef : 0; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the last known status code. + */ + Int32 Status() const + { + return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CCStr ErrStr() const + { + if (m_Hnd) + { + return m_Hnd->mConn.ErrStr(); + } + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CCStr ErrMsg() const + { + if (m_Hnd) + { + return m_Hnd->mConn.ErrMsg(); + } + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 ErrNo() const + { + if (m_Hnd) + { + return m_Hnd->mConn.ErrNo(); + } + return SQLITE_NOMEM; + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 ExErrNo() const + { + if (m_Hnd) + { + return m_Hnd->mConn.ExErrNo(); + } + return SQLITE_NOMEM; + } +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_HANDLE_STATEMENT_HPP_ diff --git a/modules/sqlite/Module.cpp b/modules/sqlite/Module.cpp index 18326437..6973f2a3 100644 --- a/modules/sqlite/Module.cpp +++ b/modules/sqlite/Module.cpp @@ -1,37 +1,19 @@ -// -------------------------------------------------------------------------------------------- -#include "Module.hpp" +// ------------------------------------------------------------------------------------------------ #include "Common.hpp" #include "Connection.hpp" #include "Statement.hpp" #include "Column.hpp" +#include "Transaction.hpp" -// -------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ #include #include -#include - -// -------------------------------------------------------------------------------------------- -#include - -// -------------------------------------------------------------------------------------------- -#if defined(WIN32) || defined(_WIN32) - #include -#endif +// ------------------------------------------------------------------------------------------------ namespace SqMod { -// -------------------------------------------------------------------------------------------- -PluginFuncs* _Func = nullptr; -PluginCallbacks* _Clbk = nullptr; -PluginInfo* _Info = nullptr; - -// -------------------------------------------------------------------------------------------- -HSQAPI _SqAPI = nullptr; -HSQEXPORTS _SqMod = nullptr; -HSQUIRRELVM _SqVM = nullptr; - /* ------------------------------------------------------------------------------------------------ - * Bind speciffic functions to certain server events. + * Bind specific functions to certain server events. */ void BindCallbacks(); @@ -40,22 +22,22 @@ void BindCallbacks(); */ void UnbindCallbacks(); -/* -------------------------------------------------------------------------------------------- +/* ------------------------------------------------------------------------------------------------ * Register the module API under the specified virtual machine. */ void RegisterAPI(HSQUIRRELVM vm); -/* -------------------------------------------------------------------------------------------- - * Initialize the plugin by obtaining the API provided by the host plugin. +/* ------------------------------------------------------------------------------------------------ + * Initialize the plug-in by obtaining the API provided by the host plug-in. */ void OnSquirrelInitialize() { - // Attempt to import the plugin API exported by the host plugin + // Attempt to import the plug-in API exported by the host plug-in _SqMod = sq_api_import(_Func); - // Did we failed to obtain the plugin exports? - if(!_SqMod) + // Did we failed to obtain the plug-in exports? + if (!_SqMod) { - OutputError("Failed to attach [%s] on host plugin.", SQSQLITE_NAME); + OutputError("Failed to attach [%s] on host plug-in.", SQSQLITE_NAME); } else { @@ -66,12 +48,12 @@ void OnSquirrelInitialize() } } -/* -------------------------------------------------------------------------------------------- +/* ------------------------------------------------------------------------------------------------ * Load the module on the virtual machine provided by the host module. */ void OnSquirrelLoad() { - // Make sure that we have a valid plugin API + // Make sure that we have a valid plug-in API if (!_SqMod) { return; // Unable to proceed! @@ -91,23 +73,28 @@ void OnSquirrelLoad() OutputMessage("Registered: %s", SQSQLITE_NAME); } -/* -------------------------------------------------------------------------------------------- +/* ------------------------------------------------------------------------------------------------ * The virtual machine is about to be terminated and script resources should be released. */ void OnSquirrelTerminate() { OutputMessage("Terminating: %s", SQSQLITE_NAME); + // Release null objects just in case + NullObject().Release(); + NullTable().Release(); + NullArray().Release(); + NullFunction().ReleaseGently(); // Release the current virtual machine, if any DefaultVM::Set(nullptr); } -/* -------------------------------------------------------------------------------------------- +/* ------------------------------------------------------------------------------------------------ * Validate the module API to make sure we don't run into issues. */ bool CheckAPIVer(CCStr ver) { // Obtain the numeric representation of the API version - long vernum = std::strtol(ver, nullptr, 10); + const LongI vernum = std::strtol(ver, nullptr, 10); // Check against version mismatch if (vernum == SQMOD_API_VER) { @@ -120,8 +107,8 @@ bool CheckAPIVer(CCStr ver) return false; } -/* -------------------------------------------------------------------------------------------- - * React to command sent by other plugins. +/* ------------------------------------------------------------------------------------------------ + * React to command sent by other plug-ins. */ static uint8_t OnPluginCommand(uint32_t command_identifier, CCStr message) { @@ -144,8 +131,8 @@ static uint8_t OnPluginCommand(uint32_t command_identifier, CCStr message) return 1; } -/* -------------------------------------------------------------------------------------------- - * The server was initialized and this plugin was loaded successfully. +/* ------------------------------------------------------------------------------------------------ + * The server was initialized and this plug-in was loaded successfully. */ static uint8_t OnServerInitialise() { @@ -174,7 +161,7 @@ void UnbindCallbacks() _Clbk->OnPluginCommand = nullptr; } -// -------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ void RegisterAPI(HSQUIRRELVM vm) { Table sqlns(vm); @@ -185,7 +172,7 @@ void RegisterAPI(HSQUIRRELVM vm) .Ctor< CCStr >() .Ctor< CCStr, Int32 >() .Ctor< CCStr, Int32, CCStr >() - // Metamethods + // Meta-methods .Func(_SC("_cmp"), &Connection::Cmp) .SquirrelFunc(_SC("_typename"), &Connection::Typename) .Func(_SC("_tostring"), &Connection::ToString) @@ -243,7 +230,7 @@ void RegisterAPI(HSQUIRRELVM vm) .Ctor() .Ctor< const Connection &, CCStr >() .Ctor< const Statement & >() - // Metamethods + // Meta-methods .Func(_SC("_cmp"), &Statement::Cmp) .SquirrelFunc(_SC("_typename"), &Statement::Typename) .Func(_SC("_tostring"), &Statement::ToString) @@ -312,7 +299,7 @@ void RegisterAPI(HSQUIRRELVM vm) // Constructors .Ctor() .Ctor< const Column & >() - // Metamethods + // Meta-methods .Func(_SC("_cmp"), &Column::Cmp) .SquirrelFunc(_SC("_typename"), &Column::Typename) .Func(_SC("_tostring"), &Column::ToString) @@ -702,119 +689,33 @@ void RegisterAPI(HSQUIRRELVM vm) ); } -// -------------------------------------------------------------------------------------------- -void OutputMessageImpl(const char * msg, va_list args) -{ -#if defined(WIN32) || defined(_WIN32) - HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); - - CONSOLE_SCREEN_BUFFER_INFO csb_before; - GetConsoleScreenBufferInfo( hstdout, &csb_before); - SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN); - std::printf("[SQMOD] "); - - SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); - std::vprintf(msg, args); - std::puts(""); - - SetConsoleTextAttribute(hstdout, csb_before.wAttributes); -#else - std::printf("%c[0;32m[SQMOD]%c[0m", 27, 27); - std::vprintf(msg, args); - std::puts(""); -#endif -} - -// -------------------------------------------------------------------------------------------- -void OutputErrorImpl(const char * msg, va_list args) -{ -#if defined(WIN32) || defined(_WIN32) - HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); - - CONSOLE_SCREEN_BUFFER_INFO csb_before; - GetConsoleScreenBufferInfo( hstdout, &csb_before); - SetConsoleTextAttribute(hstdout, FOREGROUND_RED | FOREGROUND_INTENSITY); - std::printf("[SQMOD] "); - - SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); - std::vprintf(msg, args); - std::puts(""); - - SetConsoleTextAttribute(hstdout, csb_before.wAttributes); -#else - std::printf("%c[0;91m[SQMOD]%c[0m", 27, 27); - std::vprintf(msg, args); - std::puts(""); -#endif -} - -// -------------------------------------------------------------------------------------------- -void OutputDebug(const char * msg, ...) -{ -#ifdef _DEBUG - // Initialize the arguments list - va_list args; - va_start(args, msg); - // Call the output function - OutputMessageImpl(msg, args); - // Finalize the arguments list - va_end(args); -#else - SQMOD_UNUSED_VAR(msg); -#endif -} - -// -------------------------------------------------------------------------------------------- -void OutputMessage(const char * msg, ...) -{ - // Initialize the arguments list - va_list args; - va_start(args, msg); - // Call the output function - OutputMessageImpl(msg, args); - // Finalize the arguments list - va_end(args); -} - -// -------------------------------------------------------------------------------------------- -void OutputError(const char * msg, ...) -{ - // Initialize the arguments list - va_list args; - va_start(args, msg); - // Call the output function - OutputErrorImpl(msg, args); - // Finalize the arguments list - va_end(args); -} - } // Namespace:: SqMod -// -------------------------------------------------------------------------------------------- -SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs* functions, PluginCallbacks* callbacks, PluginInfo* info) +// ------------------------------------------------------------------------------------------------ +SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * functions, PluginCallbacks * callbacks, PluginInfo * info) { using namespace SqMod; - // Output plugin header + // Output plug-in header puts(""); OutputMessage("--------------------------------------------------------------------"); - OutputMessage("Plugin: %s", SQSQLITE_NAME); + OutputMessage("Plug-in: %s", SQSQLITE_NAME); OutputMessage("Author: %s", SQSQLITE_AUTHOR); OutputMessage("Legal: %s", SQSQLITE_COPYRIGHT); OutputMessage("--------------------------------------------------------------------"); puts(""); - // Attempt to find the host plugin ID - int host_plugin_id = functions->FindPlugin((char *)(SQMOD_HOST_NAME)); - // See if our plugin was loaded after the host plugin + // Attempt to find the host plug-in ID + const int host_plugin_id = functions->FindPlugin(SQMOD_HOST_NAME); + // See if our plug-in was loaded after the host plug-in if (host_plugin_id < 0) { - OutputError("%s could find the host plugin", SQSQLITE_NAME); + OutputError("%s could find the host plug-in", SQSQLITE_NAME); // Don't load! return SQMOD_FAILURE; } // Should never reach this point but just in case else if (static_cast< Uint32 >(host_plugin_id) > info->pluginId) { - OutputError("%s loaded after the host plugin", SQSQLITE_NAME); + OutputError("%s loaded after the host plug-in", SQSQLITE_NAME); // Don't load! return SQMOD_FAILURE; } @@ -822,15 +723,15 @@ SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs* functions, PluginCallb _Func = functions; _Clbk = callbacks; _Info = info; - // Assign plugin version + // Assign plug-in version _Info->pluginVersion = SQSQLITE_VERSION; _Info->apiMajorVersion = PLUGIN_API_MAJOR; _Info->apiMinorVersion = PLUGIN_API_MINOR; - // Assign the plugin name + // Assign the plug-in name std::snprintf(_Info->name, sizeof(_Info->name), "%s", SQSQLITE_HOST_NAME); // Bind callbacks BindCallbacks(); - // Notify that the plugin was successfully loaded + // Notify that the plug-in was successfully loaded OutputMessage("Successfully loaded %s", SQSQLITE_NAME); // Dummy spacing puts(""); diff --git a/modules/sqlite/Module.hpp b/modules/sqlite/Module.hpp deleted file mode 100644 index d6c041b1..00000000 --- a/modules/sqlite/Module.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _SQSQLITE_MODULE_HPP_ -#define _SQSQLITE_MODULE_HPP_ - -// ------------------------------------------------------------------------------------------------ -#include "SqMod.h" - -// ------------------------------------------------------------------------------------------------ -namespace SqMod { - -/* ------------------------------------------------------------------------------------------------ - * Proxies to comunicate with the server. -*/ -extern PluginFuncs* _Func; -extern PluginCallbacks* _Clbk; -extern PluginInfo* _Info; - -/* ------------------------------------------------------------------------------------------------ - * Proxies to comunicate with the Squirrel plugin. -*/ -extern HSQAPI _SqAPI; -extern HSQEXPORTS _SqMod; -extern HSQUIRRELVM _SqVM; - -/* ------------------------------------------------------------------------------------------------ - * Output a message only if the _DEBUG was defined. -*/ -void OutputDebug(const char * msg, ...); - -/* ------------------------------------------------------------------------------------------------ - * Output a formatted user message to the console. -*/ -void OutputMessage(const char * msg, ...); - -/* ------------------------------------------------------------------------------------------------ - * Output a formatted error message to the console. -*/ -void OutputError(const char * msg, ...); - -} // Namespace:: SqMod - -#endif // _SQSQLITE_MODULE_HPP_ diff --git a/modules/sqlite/Statement.cpp b/modules/sqlite/Statement.cpp index 6d37c308..d143a17a 100644 --- a/modules/sqlite/Statement.cpp +++ b/modules/sqlite/Statement.cpp @@ -2,14 +2,10 @@ #include "Statement.hpp" #include "Connection.hpp" #include "Column.hpp" -#include "Module.hpp" // ------------------------------------------------------------------------------------------------ #include -// ------------------------------------------------------------------------------------------------ -#include - // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Statement.hpp b/modules/sqlite/Statement.hpp index cb644e08..c9db74d1 100644 --- a/modules/sqlite/Statement.hpp +++ b/modules/sqlite/Statement.hpp @@ -2,8 +2,7 @@ #define _SQSQLITE_STATEMENT_HPP // ------------------------------------------------------------------------------------------------ -#include "Common.hpp" -#include "SqAPI.h" +#include "Handle/Statement.hpp" // ------------------------------------------------------------------------------------------------ namespace SqMod { diff --git a/modules/sqlite/Transaction.cpp b/modules/sqlite/Transaction.cpp new file mode 100644 index 00000000..12231709 --- /dev/null +++ b/modules/sqlite/Transaction.cpp @@ -0,0 +1,72 @@ +// ------------------------------------------------------------------------------------------------ +#include "Transaction.hpp" +#include "Connection.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +Transaction::Transaction(const Connection & db) + : Transaction(db.GetHandle()) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +Transaction::Transaction(const ConnHnd & db) + : m_Connection(db), m_Committed(false) +{ + // Was the specified database connection valid? + if (!m_Connection) + { + STHROWF("Invalid connection handle"); + } + // Attempt to begin transaction + else if ((m_Connection = sqlite3_exec(m_Connection, "BEGIN", nullptr, nullptr, nullptr)) != SQLITE_OK) + { + STHROWF("Unable to begin transaction [%s]", m_Connection.ErrMsg()); + } +} + +// ------------------------------------------------------------------------------------------------ +Transaction::~Transaction() +{ + // Was this transaction successfully committed? + if (m_Committed) + { + return; // We're done here! + } + // Attempt to roll back changes because this failed to commit + if ((m_Connection = sqlite3_exec(m_Connection, "ROLLBACK", nullptr, nullptr, nullptr)) != SQLITE_OK) + { + STHROWF("Unable to rollback transaction [%s]", m_Connection.ErrMsg()); + } +} + +// ------------------------------------------------------------------------------------------------ +bool Transaction::Commit() +{ + // We shouldn't even be here if there wasn't a valid connection but let's be sure + if (!m_Connection) + { + STHROWF("Invalid database connection"); + } + // Was this transaction already committed? + else if (m_Committed) + { + STHROWF("Transaction was already committed"); + } + // Attempt to commit the change during this transaction + else if ((m_Connection = sqlite3_exec(m_Connection, "COMMIT", nullptr, nullptr, nullptr)) != SQLITE_OK) + { + STHROWF("Unable to commit transaction [%s]", m_Connection.ErrMsg()); + } + else + { + m_Committed = true; // Everything was committed successfully + } + // Return the result + return m_Committed; +} + +} // Namespace:: SqMod diff --git a/modules/sqlite/Transaction.hpp b/modules/sqlite/Transaction.hpp new file mode 100644 index 00000000..09b4b14f --- /dev/null +++ b/modules/sqlite/Transaction.hpp @@ -0,0 +1,74 @@ +#ifndef _SQSQLITE_TRANSACTION_HPP_ +#define _SQSQLITE_TRANSACTION_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Handle/Connection.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Implements the RAII pattern for database transactions. +*/ +class Transaction +{ +public: + + /* -------------------------------------------------------------------------------------------- + * Construct by taking the handle from a connection. + */ + Transaction(const Connection & db); + + /* -------------------------------------------------------------------------------------------- + * Construct using the direct connection handle. + */ + Transaction(const ConnHnd & db); + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. (disabled) + */ + Transaction(const Transaction & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. (disabled) + */ + Transaction(Transaction && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~Transaction(); + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. (disabled) + */ + Transaction & operator = (const Transaction & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. (disabled) + */ + Transaction & operator = (Transaction && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Attempt to commit changes to the database. + */ + bool Commit(); + + /* -------------------------------------------------------------------------------------------- + * See whether the change during this transaction were successfully committed. + */ + bool Commited() const + { + return m_Committed; + } + +private: + + // -------------------------------------------------------------------------------------------- + ConnHnd m_Connection; // The database connection handle where the transaction began. + bool m_Committed; // Whether changes were successfully committed to the database. +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_TRANSACTION_HPP_