diff --git a/cbp/ModSQLite.cbp b/cbp/ModSQLite.cbp new file mode 100644 index 00000000..e3a6e89e --- /dev/null +++ b/cbp/ModSQLite.cbp @@ -0,0 +1,393 @@ + + + + + + diff --git a/modules/sqlite/Column.cpp b/modules/sqlite/Column.cpp new file mode 100644 index 00000000..65d3b7ef --- /dev/null +++ b/modules/sqlite/Column.cpp @@ -0,0 +1,264 @@ +// ------------------------------------------------------------------------------------------------ +#include "Column.hpp" +#include "Connection.hpp" +#include "Statement.hpp" +#include "Module.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQInteger Column::Typename(HSQUIRRELVM vm) +{ + static SQChar name[] = _SC("SqSQLiteColumn"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +bool Column::Validate() const +{ + // Are we pointing to a valid index? + if (m_Index < 0) + _SqMod->SqThrow("Invalid column index"); + // Do we belong to a valid statement? + else if (!m_Stmt) + _SqMod->SqThrow("Invalid SQLite statement reference"); + // Requirements satisfied + else + return true; + // Validation failed + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool Column::RowAvailable() const +{ + // Are we pointing to a valid index? + if (m_Index < 0) + _SqMod->SqThrow("Invalid column index"); + // Do we belong to a valid statement? + else if (!m_Stmt) + _SqMod->SqThrow("Invalid SQLite statement reference"); + // Do we have any rows available? + else if (!m_Stmt->mGood) + _SqMod->SqThrow("No row available"); + // Requirements satisfied + else + return true; + // Validation failed + return false; +} + +// ------------------------------------------------------------------------------------------------ +Statement Column::GetStatement() const +{ + // Validate the handle + if (Validate()) + return Statement(m_Stmt); + // Request failed + return Statement(); +} + +// ------------------------------------------------------------------------------------------------ +Connection Column::GetConnection() const +{ + // Validate the handle + if (Validate()) + return Connection(m_Stmt->mConn); + // Request failed + return Connection(); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Column::GetNumber() const +{ + // Validate the handle and index + if (RowAvailable()) + return sqlite3_column_int(m_Stmt, m_Index); + // Request failed + return 0; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Column::GetInteger() const +{ + // Validate the handle and index + if (RowAvailable()) +#ifdef _SQ64 + return sqlite3_column_int64(m_Stmt, m_Index); +#else + return sqlite3_column_int(m_Stmt, m_Index); +#endif + // Request failed + return 0; +} + +// ------------------------------------------------------------------------------------------------ +SQFloat Column::GetFloat() const +{ + // Validate the handle and index + if (RowAvailable()) + return (SQFloat)sqlite3_column_double(m_Stmt, m_Index); + // Request failed + return SQFloat(0.0); +} + +// ------------------------------------------------------------------------------------------------ +Object Column::GetLong() const +{ + // Validate the handle and index + if (!RowAvailable()) + return Object(); // Request failed + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push a long integer instance with the requested value on the stack + _SqMod->PushSLongObject(_SqVM, sqlite3_column_int64(m_Stmt, m_Index)); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +Object Column::GetString() const +{ + // Validate the handle and index + if (RowAvailable()) + return Object(); // Request failed + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push the column text on the stack + sq_pushstring(_SqVM, (CSStr)sqlite3_column_text(m_Stmt, m_Index), + sqlite3_column_bytes(m_Stmt, m_Index)); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +bool Column::GetBoolean() const +{ + // Validate the handle and index + if (RowAvailable()) + return sqlite3_column_int(m_Stmt, m_Index) > 0; + // Request failed + return false; +} + +// ------------------------------------------------------------------------------------------------ +Object Column::GetBlob() const +{ + // Validate the handle and index + if (RowAvailable()) + return Object(); // Request failed + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Obtain the size of the data + const Int32 sz = sqlite3_column_bytes(m_Stmt, m_Index); + // Allocate a blob of the same size + SQUserPointer p = sqstd_createblob(_SqVM, sz); + // Obtain a pointer to the data + const void * b = sqlite3_column_blob(m_Stmt, m_Index); + // Could the memory blob be allocated? + if (!p) + { + _SqMod->SqThrow("Unable to allocate space for column blob value"); + // Request failed + return Object(); + } + // Is there any data to read? + else if (!b) + { + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Now throw the error + _SqMod->SqThrow("Unable to read data from column blob value"); + // Request failed + return Object(); + } + // Copy the data into the memory blob + else + memcpy(p, b, sz); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +SQChar Column::GetChar() const +{ + // Validate the handle and index + if (RowAvailable()) + return (SQChar)sqlite3_column_int(m_Stmt, m_Index); + // Request failed + return 0; +} + +// ------------------------------------------------------------------------------------------------ +bool Column::IsNull() const +{ + // Can we make the request? + if (Validate()) + return (sqlite3_column_type(m_Stmt, m_Index) == SQLITE_NULL); + // Request failed + return true; +} + +// ------------------------------------------------------------------------------------------------ +CSStr Column::GetName() const +{ + // Can we make the request? + if (Validate()) + return sqlite3_column_name(m_Stmt, m_Index); + // Request failed + return _SC(""); +} + +// ------------------------------------------------------------------------------------------------ +CSStr Column::GetOriginName() const +{ +#ifdef SQLITE_ENABLE_COLUMN_METADATA + // Can we make the request? + if (Validate()) + return sqlite3_column_origin_name(m_Stmt, m_Index); +#else + _SqMod->SqThrow("The module was compiled without this feature"); +#endif + // Request failed + return _SC(""); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Column::GetType() const +{ + // Can we make the request? + if (Validate()) + return sqlite3_column_type(m_Stmt, m_Index); + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Int32 Column::GetBytes() const +{ + // Can we make the request? + if (Validate()) + return sqlite3_column_bytes(m_Stmt, m_Index); + // Request failed + return -1; +} + + +} // Namespace:: SqMod diff --git a/modules/sqlite/Column.hpp b/modules/sqlite/Column.hpp new file mode 100644 index 00000000..c1e0368c --- /dev/null +++ b/modules/sqlite/Column.hpp @@ -0,0 +1,245 @@ +#ifndef _SQSQLITE_COLUMN_HPP_ +#define _SQSQLITE_COLUMN_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" +#include "SqAPI.h" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Helper class used to manage statement columns. +*/ +class Column +{ + // -------------------------------------------------------------------------------------------- + friend class Statement; + +private: + + // -------------------------------------------------------------------------------------------- + Int32 m_Index; // The index of the managed column. + + // -------------------------------------------------------------------------------------------- + StmtHnd m_Stmt; // The statement where the column exist. + + /* -------------------------------------------------------------------------------------------- + * Validate the statement reference and index, and throw an error if they're invalid. + */ + bool Validate() const; + + /* -------------------------------------------------------------------------------------------- + * Validate the statement reference, index and row, and throw an error if they're invalid. + */ + bool RowAvailable() const; + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + Column(const StmtHnd & stmt, Int32 idx) + : m_Index(idx), m_Stmt(stmt) + { + /* ... */ + } + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor (null). + */ + Column() + : m_Index(-1), m_Stmt() + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Column(const Column & o) + : m_Index(o.m_Index), m_Stmt(o.m_Stmt) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~Column() + { + /* Let the reference manager destroy the statement when necessary. */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Column operator = (const Column & o) + { + m_Index = o.m_Index; + m_Stmt = o.m_Stmt; + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison between two connection handles. + */ + bool operator == (const Column & o) const + { + return (m_Index == o.m_Index); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison between two connection handles. + */ + bool operator != (const Column & o) const + { + return (m_Index != o.m_Index); + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to boolean for use in boolean operations. + */ + operator bool () const + { + return m_Index >= 0; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to compare two instances of this type. + */ + Int32 Cmp(const Column & o) const + { + if (m_Stmt == o.m_Stmt) + return 0; + else if (m_Stmt.HndPtr() > o.m_Stmt.HndPtr()) + return 1; + else + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + CSStr ToString() const + { + return FmtStr(_SC("%d"), m_Index); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this statement is valid. + */ + bool IsValid() const + { + return (bool)m_Stmt; // An invalid statement means an invalid column + } + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to this statement handle. + */ + Uint32 GetRefCount() const + { + return m_Stmt.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated column index. + */ + Int32 GetIndex() const + { + return m_Index; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated database statement. + */ + Statement GetStatement() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated database connection. + */ + Connection GetConnection() const; + + /* -------------------------------------------------------------------------------------------- + * Release the reference to the associated database statement and index. + */ + void Release() + { + m_Stmt = StmtHnd(); + m_Index = -1; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a 32bit integer. + */ + Int32 GetNumber() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a native script integer. + */ + SQInteger GetInteger() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a native script floating point. + */ + SQFloat GetFloat() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a long integer. + */ + Object GetLong() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a string. + */ + Object GetString() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a boolean. + */ + bool GetBoolean() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a memory blob. + */ + Object GetBlob() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value inside the associated column cel as a character. + */ + SQChar GetChar() const; + + /* -------------------------------------------------------------------------------------------- + * Check whether the associated column is null. + */ + bool IsNull() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the name of the associated column index. + */ + CSStr GetName() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column origin name if the library was compiled with such feature. + */ + CSStr GetOriginName() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the type identifier of the associated column index. + */ + Int32 GetType() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the size in bytes of the associated column index. + */ + Int32 GetBytes() const; +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_COLUMN_HPP_ diff --git a/modules/sqlite/Common.cpp b/modules/sqlite/Common.cpp new file mode 100644 index 00000000..438297b8 --- /dev/null +++ b/modules/sqlite/Common.cpp @@ -0,0 +1,427 @@ +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" +#include "Module.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +static SQChar g_Buffer[4096]; // Common buffer to reduce memory allocations. + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +CSStr FmtStr(CSStr str, ...) +{ + // Initialize the argument list + va_list args; + va_start (args, str); + // Write the requested contents + if (snprintf(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, ...) +{ + // Initialize the argument list + va_list args; + va_start (args, str); + // Write the requested contents + sqlite3_vsnprintf(sizeof(g_Buffer), g_Buffer, str, args); + // Release the argument list + va_end(args); + // Return the data from the buffer + return g_Buffer; +} + +// ------------------------------------------------------------------------------------------------ +bool IsQueryEmpty(CSStr str) +{ + // Is the pointer valid? + if (!str) + return true; + // Currently processed character + SQChar c = 0; + // See if the query contains any alpha numeric characters + while ((c = *str) != 0) + { + if (isalnum(c) != 0) + return false; + ++str; + } + // At this point we consider the query empty + return true; +} + +// ------------------------------------------------------------------------------------------------ +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."); + 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) + { + _SqMod->SqThrow("Unable to connect to database. Database already connected"); + // Unable to proceed + return; + } + // Make sure the name is valid + else if (!name || strlen(name) <= 0) + { + _SqMod->SqThrow("Unable to connect to database. The name is invalid"); + // Unable to proceed + return; + } + // Attempt to create the database connection + else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK) + { + // Must be destroyed regardless of result + sqlite3_close(mPtr); + // Explicitly make sure it's null + mPtr = NULL; + // Now its safe to throw the error + _SqMod->SqThrow("Unable to connect to database [%s]", sqlite3_errstr(mStatus)); + // Unable to proceed + return; + } + // 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; + // Is there anything to flush? + else if (!num || mQueue.empty()) + return 0; + // 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", NULL, NULL, NULL)) != SQLITE_OK) + { + _SqMod->SqThrow("Unable to begin transaction [%s]", sqlite3_errmsg(mPtr)); + // Unable to proceed + return -1; + } + // 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(), NULL, NULL, NULL)) == SQLITE_OK) + continue; + // Do we have to execute any callback to resolve our issue? + else if (!callback.IsNull()) + { + // Ask the callback whether the query processing should end here + SharedPtr< bool > ret = callback.Evaluate< bool, Int32, CSStr >(mStatus, itr->c_str()); + // Should we break here? + if (!!ret && (*ret == false)) + break; + } + } + // 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", NULL, NULL, NULL)) == SQLITE_OK) + return sqlite3_changes(mPtr); + // Attempt to roll back erroneous changes + else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to rollback transaction [%s]", sqlite3_errmsg(mPtr)); + // The transaction failed somehow but we managed to rollback + else + _SqMod->SqThrow("Unable to commit transaction because [%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."); + 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) + { + _SqMod->SqThrow("Unable to prepare statement. Statement already prepared"); + // Unable to proceed + return; + } + // Is the specified database connection is valid? + else if (!mConn) + { + _SqMod->SqThrow("Unable to prepare statement. Invalid connection handle"); + // Unable to proceed + return; + } + // 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()) + _SqMod->SqThrow("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, NULL)) != SQLITE_OK) + { + // Clear the query string since it failed + mQuery.clear(); + // Now it's safe to throw the error + _SqMod->SqThrow("Unable to prepare statement [%s]", mConn.ErrMsg()); + // Explicitly make sure the handle is null + mPtr = NULL; + } + else + // Obtain the number of available columns + mColumns = sqlite3_column_count(mPtr); +} + +// ------------------------------------------------------------------------------------------------ +Int32 StmtHnd::Handle::GetColumnIndex(CSStr name) +{ + // Validate the handle + if (!mPtr) + _SqMod->SqThrow("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) + _SqMod->SqThrow("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; +} + +// ------------------------------------------------------------------------------------------------ +void SetSoftHeapLimit(Int32 limit) +{ + sqlite3_soft_heap_limit(limit); +} + +// ------------------------------------------------------------------------------------------------ +Int32 ReleaseMemory(Int32 bytes) +{ + return sqlite3_release_memory(bytes); +} + +// ------------------------------------------------------------------------------------------------ +Object GetMemoryUsage() +{ + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push a long integer instance with the requested value on the stack + _SqMod->PushSLongObject(_SqVM, sqlite3_memory_used()); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +Object GetMemoryHighwaterMark(bool reset) +{ + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push a long integer instance with the requested value on the stack + _SqMod->PushSLongObject(_SqVM, sqlite3_memory_highwater(reset)); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +CSStr EscapeString(CSStr str) +{ + // Is there even a string to escape? + if (!str) + return _SC(""); // Default to empty string + // Attempt to escape the specified string + sqlite3_snprintf(sizeof(g_Buffer), g_Buffer, "%q", str); + // Return the resulted string + return g_Buffer; +} + +// ------------------------------------------------------------------------------------------------ +CCStr EscapeStringEx(SQChar spec, CCStr str) +{ + // Utility that allows changing the format specifier temporarily + static SQChar fs[] = _SC("%q"); + // Validate the specified format specifier + if (spec != 'q' && spec != 'Q' && spec != 'w' && spec != 's') + { + _SqMod->SqThrow("Unknown format specifier: %c", spec); + // Default to empty string + return _SC(""); + } + // Is there even a string to escape? + else if (!str) + return _SC(""); // Default to empty string + // Apply the format specifier + fs[1] = spec; + // Attempt to escape the specified string + sqlite3_snprintf(sizeof(g_Buffer), g_Buffer, fs, str); + // Restore the format specifier + fs[1] = '1'; + // Return the resulted string + return g_Buffer; +} + +// ------------------------------------------------------------------------------------------------ +CCStr ArrayToQueryColumns(Array & arr) +{ + // Do we even have any elements to process? + if (arr.Length() <= 0) + return _SC(""); // Default to empty string + // Allocate a vector with the required amount of column names + std::vector< String > values(arr.Length()); + // Attempt to extract the array elements as strings + arr.GetArray< String >(&values[0], values.size()); + // Used to know the position of the next column name + Uint32 offset = 0; + // 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) + { + // Is the name valid? + if (itr->empty()) + { + _SqMod->SqThrow("Invalid column name"); + // Default to empty string + return _SC(""); + } + // Attempt to append the column name to the buffer + sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + 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 + offset += 2; + } + // Trim the last coma and space + if (offset >= 2) + g_Buffer[offset-2] = 0; + else + g_Buffer[0] = 0; + // Return the resulted string + return g_Buffer; +} + +CCStr TableToQueryColumns(Table & tbl) +{ + // Used to know the position of the next column name + Uint32 offset = 0; + // Used to obtain the column name temporarily + String name; + // Obtain the start of the table + Table::iterator itr; + // Process all elements within range + while (tbl.Next(itr)) + { + // Use the element key as the column name + name.assign(itr.getName()); + // Is the name valid? + if (name.empty()) + { + _SqMod->SqThrow("Invalid column name"); + // Default to empty string + return _SC(""); + } + // Attempt to append the column name to the buffer + sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + 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 + offset += 2; + } + // Trim the last coma and space + if (offset >= 2) + g_Buffer[offset-2] = 0; + else + g_Buffer[0] = 0; + // Return the resulted string + return g_Buffer; +} + +} // Namespace:: SqMod diff --git a/modules/sqlite/Common.hpp b/modules/sqlite/Common.hpp new file mode 100644 index 00000000..e6be9c42 --- /dev/null +++ b/modules/sqlite/Common.hpp @@ -0,0 +1,770 @@ +#ifndef _SQSQLITE_COMMON_HPP_ +#define _SQSQLITE_COMMON_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "ModBase.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Forward declarations. +*/ +class Connection; +class Statement; +class Column; +class Transaction; + +/* ------------------------------------------------------------------------------------------------ + * SOFTWARE INFORMATION +*/ +#define SQSQLITE_NAME "Squirrel SQLite Module" +#define SQSQLITE_AUTHOR "Sandu Liviu Catalin (S.L.C)" +#define SQSQLITE_COPYRIGHT "Copyright (C) 2016 Sandu Liviu Catalin" +#define SQSQLITE_HOST_NAME "SqModSQLiteHost" +#define SQSQLITE_VERSION 001 +#define SQSQLITE_VERSION_STR "0.0.1" +#define SQSQLITE_VERSION_MAJOR 0 +#define SQSQLITE_VERSION_MINOR 0 +#define SQSQLITE_VERSION_PATCH 1 + +/* ------------------------------------------------------------------------------------------------ + * Generate a formatted string. +*/ +CSStr FmtStr(CSStr str, ...); + +/* ------------------------------------------------------------------------------------------------ + * Generate a formatted query. +*/ +CSStr QFmtStr(CSStr str, ...); + +/* ------------------------------------------------------------------------------------------------ + * Tests if a certain query string is empty. +*/ +bool IsQueryEmpty(CSStr str); + +/* ------------------------------------------------------------------------------------------------ + * 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. + +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(NULL) + , 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) : NULL) + { + /* ... */ + } + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor (null). + */ + ConnHnd() + : m_Hnd(NULL) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + ConnHnd(const ConnHnd & o) + : m_Hnd(o.m_Hnd) + + { + Grab(); + } + + /* -------------------------------------------------------------------------------------------- + * 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; + } + + /* -------------------------------------------------------------------------------------------- + * 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 : NULL; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () const + { + return m_Hnd ? m_Hnd->mPtr : NULL; + } + + /* -------------------------------------------------------------------------------------------- + * 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_Ptr); + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Indirection operator for obtaining a reference of the managed pointer. + */ + Handle & operator * () const + { + assert(m_Ptr); + 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 + { + assert(m_Ptr); + return m_Hnd ? m_Hnd->mRef : 0; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CCStr ErrStr() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr)); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CCStr ErrMsg() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return sqlite3_errmsg(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 ErrNo() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return sqlite3_errcode(m_Hnd->mPtr); + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 ExErrNo() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return sqlite3_extended_errcode(m_Hnd->mPtr); + } +}; + +/* ------------------------------------------------------------------------------------------------ + * 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. + +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(NULL) + , 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(NULL) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + StmtHnd(const StmtHnd & o) + : m_Hnd(o.m_Hnd) + + { + Grab(); + } + + /* -------------------------------------------------------------------------------------------- + * 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; + } + + /* -------------------------------------------------------------------------------------------- + * 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 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 : NULL; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the managed instance. + */ + operator Pointer () const + { + return m_Hnd ? m_Hnd->mPtr : NULL; + } + + /* -------------------------------------------------------------------------------------------- + * 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_Ptr); + return m_Hnd; + } + + /* -------------------------------------------------------------------------------------------- + * Indirection operator for obtaining a reference of the managed pointer. + */ + Handle & operator * () const + { + assert(m_Ptr); + 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 + { + assert(m_Ptr); + return m_Hnd ? m_Hnd->mRef : 0; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CCStr ErrStr() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return m_Hnd->mConn.ErrStr(); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CCStr ErrMsg() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return m_Hnd->mConn.ErrMsg(); + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 ErrNo() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return m_Hnd->mConn.ErrNo(); + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 ExErrNo() const + { + assert(m_Ptr); // SQLite does it's null pointer validations internally + return m_Hnd->mConn.ExErrNo(); + } +}; + +/* ------------------------------------------------------------------------------------------------ + * +*/ +void SetSoftHeapLimit(Int32 limit); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +Int32 ReleaseMemory(Int32 bytes); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +Object GetMemoryUsage(); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +Object GetMemoryHighwaterMark(bool reset); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +CSStr EscapeString(CSStr str); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +CCStr EscapeStringEx(SQChar spec, CCStr str); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +CCStr ArrayToQueryColumns(Array & arr); + +/* ------------------------------------------------------------------------------------------------ + * +*/ +CCStr TableToQueryColumns(Table & tbl); + +} // Namespace:: SqMod + +#endif // _SQSQLITE_COMMON_HPP_ diff --git a/modules/sqlite/Connection.cpp b/modules/sqlite/Connection.cpp new file mode 100644 index 00000000..629c2f19 --- /dev/null +++ b/modules/sqlite/Connection.cpp @@ -0,0 +1,575 @@ +// ------------------------------------------------------------------------------------------------ +#include "Connection.hpp" +#include "Statement.hpp" +#include "Module.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQInteger Connection::Typename(HSQUIRRELVM vm) +{ + static SQChar name[] = _SC("SqSQLiteConnection"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +bool Connection::Validate() const +{ + if (m_Handle) + return true; + // Invalid connection reference + _SqMod->SqThrow("Invalid SQLite connection reference"); + return false; +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection() + : m_Handle() +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(CSStr name) + : m_Handle(name) +{ + if (m_Handle.m_Hnd) + m_Handle->Create(name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(CSStr name, Int32 flags) + : m_Handle(name) +{ + if (m_Handle.m_Hnd) + m_Handle->Create(name, flags, NULL); +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(CSStr name, Int32 flags, CSStr vfs) + : m_Handle(name) +{ + if (m_Handle.m_Hnd) + m_Handle->Create(name, flags, vfs); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Connection::Exec(CSStr str) +{ + // Validate the handle + if (Validate() && (m_Handle = sqlite3_exec(m_Handle, str, NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to execute query [%s]", m_Handle.ErrMsg()); + // Return rows affected by this query + else + return sqlite3_changes(m_Handle); + // Operation failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Statement Connection::Query(CSStr str) const +{ + // Validate the handle + if (Validate()) + return Statement(m_Handle, str); + // Request failed + return Statement(); +} + +// ------------------------------------------------------------------------------------------------ +void Connection::Queue(CSStr str) +{ + // Validate the handle + if (!Validate()) + return; // Nothing to commit + // Is there a query to commit? + else if (IsQueryEmpty(str)) + _SqMod->SqThrow("No query to queue"); + // Add the specified string to the queue + else + m_Handle->mQueue.push_back(str); +} + +// ------------------------------------------------------------------------------------------------ +bool Connection::IsReadOnly() const +{ + // Validate the handle + if (!Validate()) + return false; + // Request the desired information + const int result = sqlite3_db_readonly(m_Handle, "main"); + // Verify the result + if (result == -1) + _SqMod->SqThrow("'main' is not the name of a database on connection"); + // Return the result + else + return (result != 1); + // Inexistent is same as read-only + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool Connection::TableExists(CCStr name) const +{ + // Validate the handle + if (!Validate()) + return false; + // Prepare a statement to inspect the master table + Statement stmt(m_Handle, "SELECT count(*) FROM [sqlite_master] WHERE [type]='table' AND [name]=?"); + // Could the statement be created? + if (stmt.IsValid()) + { + // Bind the specified name onto the statement parameter + stmt.IndexBindS(1, name); + // Attempt to step the statement and obtain a value + if (stmt.Step()) + return (sqlite3_column_int(stmt, 0) == 1); + } + // Assume it doesn't exist + return false; +} + +// ------------------------------------------------------------------------------------------------ +Object Connection::GetLastInsertRowID() const +{ + // Validate the handle + if (!Validate()) + return Object(); + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push a long integer instance with the requested value on the stack + _SqMod->PushSLongObject(_SqVM, sqlite3_last_insert_rowid(m_Handle)); + // Obtain the object from the stack + Var< Object > inst(_SqVM, -1); + // Removed pushed values (if any) + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Return the long integer instance + return inst.value; +} + +// ------------------------------------------------------------------------------------------------ +void Connection::SetBusyTimeout(Int32 millis) +{ + // Validate the handle and apply requested timeout + if (Validate() && ((m_Handle = sqlite3_busy_timeout(m_Handle, millis)) != SQLITE_OK)) + _SqMod->SqThrow("Unable to set busy timeout [%s]", m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Connection::GetInfo(Int32 operation, bool highwater, bool reset) +{ + // Don't even bother to continue if there's no valid connection handle + if (!Validate()) + return -1; + // Where to retrieve the information + Int32 cur_value; + Int32 hiwtr_value; + // Attempt to retrieve the specified information + if((m_Handle = sqlite3_db_status(m_Handle, operation, &cur_value, &hiwtr_value, reset)) != SQLITE_OK) + _SqMod->SqThrow("Unable to get runtime status information", m_Handle.ErrMsg()); + // Return what was requested + else if (highwater) + return hiwtr_value; + else + return cur_value; + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Connection Connection::CopyToMemory() +{ + // Validate the handle + if (!Validate()) + return Connection(); + // Is the database already in memory? + else if (m_Handle->mMemory) + { + _SqMod->SqThrow("The database is already in memory"); + // No reason to move it again + return Connection(); + } + // Destination database + ConnHnd db(_SC("")); + // Attempt to open an in-memory database + db->Create(_SC(":memory:"), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + // See if the database could be opened + if (!db) + // The creation process already generated the error + return Connection(); + // Clear any previous error (there shouldn't be any but just in case) + Sqrat::Error::Clear(_SqVM); + // Begin a transaction to replicate the schema of origin database + if ((m_Handle = sqlite3_exec(m_Handle, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to begin schema replication [%s]", m_Handle.ErrMsg()); + // Attempt to replicate the schema of origin database to the in-memory one + else if ((m_Handle = sqlite3_exec(m_Handle, + "SELECT [sql] FROM [sqlite_master] WHERE [sql] NOT NULL AND [tbl_name] != 'sqlite_sequence'", + &Connection::ProcessDDLRow, db->mPtr, NULL)) != SQLITE_OK) + { + // Did the error occurred from the DDL process function? + if (Sqrat::Error::Occurred(_SqVM)) + { + // Obtain the occurred message + String msg(Sqrat::Error::Message(_SqVM)); + // Throw the resulted message but also include the point where it failed + _SqMod->SqThrow("Unable to replicate schema [%s]", msg.c_str()); + } + // Obtain the message from the connection handle if possible + else + _SqMod->SqThrow("Unable to replicate schema [%s]", m_Handle.ErrMsg()); + } + // Attempt to commit the changes to the database schema replication + else if ((m_Handle = sqlite3_exec(m_Handle, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to commit schema replication [%s]", m_Handle.ErrMsg()); + // Attempt to attach the origin database to the in-memory one + else if ((db = sqlite3_exec(db, QFmtStr("ATTACH DATABASE '%q' as origin", m_Handle->mName.c_str()), + NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to attach origin [%s]", db.ErrMsg()); + // Begin a transaction to replicate the data of origin database + else if ((db = sqlite3_exec(db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK)) + _SqMod->SqThrow("Unable to begin data replication [%s]", db.ErrMsg()); + // Attempt to replicate the data of origin database to the in-memory one + else if ((db = sqlite3_exec(db, "SELECT [name] FROM [origin.sqlite_master] WHERE [type]='table'", + &Connection::ProcessDMLRow, db->mPtr, NULL)) != SQLITE_OK) + { + // Did the error occurred from the DML process function? + if (Sqrat::Error::Occurred(_SqVM)) + { + // Obtain the occurred message + String msg(Sqrat::Error::Message(_SqVM)); + // Throw the resulted message but also include the point where it failed + _SqMod->SqThrow("Unable to replicate data [%s]", msg.c_str()); + } + // Obtain the message from the connection handle if possible + else + _SqMod->SqThrow("Unable to replicate data [%s]", db.ErrMsg()); + } + // Attempt to commit the changes to the database data replication + else if ((db = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK) + { + _SqMod->SqThrow("Unable to commit data replication [%s]", db.ErrMsg()); + // Attempt to rollback changes from the data copy operation + if ((db = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to rollback data replication [%s]", db.ErrMsg()); + // Attempt to detach the disk origin from in-memory database + else if ((db = sqlite3_exec(db, "DETACH DATABASE origin", NULL, NULL, NULL)) != SQLITE_OK) + _SqMod->SqThrow("Unable to detach origin [%s]", db.ErrMsg()); + } + // At this point everything went fine and the database instance should be returned + else + return Connection(db); + // Failed to replicate the database + return Connection(); +} + +// ------------------------------------------------------------------------------------------------ +void Connection::CopyToDatabase(Connection & db) +{ + // Make sure that we have two valid database handles + if (Validate() && db.Validate()) + _SqMod->SqThrow("Invalid database connections"); + // Attempt to take the snapshot and return the result + else + TakeSnapshot(db.m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Connection::Flush(Uint32 num) +{ + // Validate the handle + if (Validate()) + { + // We need to supply a null callback + Object env; + Function func; + // Attempt to flush the requested amount of queries + return m_Handle->Flush(num, env, func); + } + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Int32 Connection::Flush(Uint32 num, Object & env, Function & func) +{ + // Validate the handle + if (Validate()) + // Attempt to flush the requested amount of queries + return m_Handle->Flush(num, env, func); + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +void Connection::TraceOutput(void * /*ptr*/, CCStr sql) +{ + _SqMod->LogInf("SQLite Trace: %s", sql); +} + +void Connection::ProfileOutput(void * /*ptr*/, CCStr sql, sqlite3_uint64 time) +{ + _SqMod->LogInf("SQLite profile (time: %llu): %s", time, sql); +} + +// ------------------------------------------------------------------------------------------------ +int Connection::ProcessDDLRow(void * db, int columns_count, char ** values, char ** /*columns*/) +{ + // Make sure that exactly one column exists in the result + if (columns_count != 1) + _SqMod->SqThrow("Error occurred during DDL: columns != 1"); + // Execute the sql statement in values[0] in the received database connection + else if (sqlite3_exec((sqlite3 *)db, values[0], NULL, NULL, NULL) != SQLITE_OK) + _SqMod->SqThrow("Error occurred during DDL execution: %s", sqlite3_errmsg((sqlite3 *)db)); + else + // Continue processing + return 0; + // Operation aborted + return -1; +} + +int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char ** /*columns*/) +{ + // Make sure that exactly one column exists in the result + if(columns_count != 1) + { + _SqMod->SqThrow("Error occurred during DML: columns != 1"); + // Operation aborted + return -1; + } + // Generate the query string with the received values + char * sql = sqlite3_mprintf("INSERT INTO main.%q SELECT * FROM origin.%q", values[0], values[0]); + // Attempt to execute the generated query string on the received database connection + if (sqlite3_exec((sqlite3 *)db, sql, NULL, NULL, NULL) != SQLITE_OK) + _SqMod->SqThrow("Error occurred during DML execution: %s", sqlite3_errmsg((sqlite3 *)db)); + else + { + // Free the generated query string + sqlite3_free(sql); + // Continue processing + return 0; + } + // Free the generated query string + sqlite3_free(sql); + // Operation aborted + return -1; +} + +// ------------------------------------------------------------------------------------------------ +void Connection::TakeSnapshot(ConnHnd & destination) +{ + // Attempt to initialize a backup structure + sqlite3_backup * backup = sqlite3_backup_init(destination, "main", m_Handle, "main"); + // See if the backup structure could be created + if (!backup) + _SqMod->SqThrow("Unable to initialize the backup structure [%s]", destination.ErrMsg()); + // -1 to copy the entire source database to the destination + else if ((m_Handle = sqlite3_backup_step(backup, -1)) != SQLITE_DONE) + _SqMod->SqThrow("Unable to copy source [%s]", m_Handle.ErrStr()); + // Clean up resources allocated by sqlite3_backup_init() + if ((m_Handle = sqlite3_backup_finish(backup)) != SQLITE_OK) + _SqMod->SqThrow("Unable to finalize backup [%s]", m_Handle.ErrStr()); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Connection::ExecF(HSQUIRRELVM vm) +{ + const Int32 top = sq_gettop(vm); + // Do we even have enough arguments? + if (top <= 1) + return sq_throwerror(vm, "Missing the query string"); + // Obtain the connection instance + Var< Connection * > inst(vm, 1); + // Do we have a valid connection instance? + if (!inst.value) + return sq_throwerror(vm, "Invalid connection instance"); + // Do we have a valid connection reference? + else if (!inst.value->m_Handle) + return sq_throwerror(vm, "Invalid SQLite connection reference"); + // Is the specified message value a string or something convertible to string? + else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1)))) + { + CCStr sql = NULL; + // Attempt to retrieve the string from the stack + if (SQ_FAILED(sq_getstring(vm, -1, &sql))) + { + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // Now we can throw the error message + return sq_throwerror(vm, "Unable to retrieve the query"); + } + // Attempt to execute the specified query + if ((inst.value->m_Handle = sqlite3_exec(inst.value->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK) + { + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // Generate the error message and throw the resulted string + return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", inst.value->m_Handle.ErrMsg())); + } + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // Push the result onto the stack + sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle)); + } + // Do we have enough values to call the format function? + else if (top > 2) + { + SStr sql = NULL; + SQInteger len = 0; + // Attempt to generate the specified string format + SQRESULT ret = sqstd_format(vm, 3, &len, &sql); + // Did the format failed? + if (SQ_FAILED(ret)) + return ret; + // Attempt to execute the resulted query + if ((inst.value->m_Handle = sqlite3_exec(inst.value->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK) + { + // Generate the error message and throw the resulted string + return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", inst.value->m_Handle.ErrMsg())); + } + // Push the result onto the stack + sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle)); + } + // All methods of retrieving the message value failed + else + return sq_throwerror(vm, "Unable to extract the query string"); + // At this point we should have a return value on the stack + return 1; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Connection::QueueF(HSQUIRRELVM vm) +{ + const Int32 top = sq_gettop(vm); + // Do we even have enough arguments? + if (top <= 1) + return sq_throwerror(vm, "Missing the query string"); + // Obtain the connection instance + Var< Connection * > inst(vm, 1); + // Do we have a valid connection instance? + if (!inst.value) + return sq_throwerror(vm, "Invalid connection instance"); + // Do we have a valid connection reference? + else if (!inst.value->m_Handle) + return sq_throwerror(vm, "Invalid SQLite connection reference"); + // Is the specified message value a string or something convertible to string? + else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1)))) + { + CCStr sql = NULL; + // Attempt to retrieve the string from the stack + if (SQ_FAILED(sq_getstring(vm, -1, &sql))) + { + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // Now we can throw the error message + return sq_throwerror(vm, "Unable to retrieve the query"); + } + // Is there even a query to queue? + else if (IsQueryEmpty(sql)) + { + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + return sq_throwerror(vm,"No query to queue"); + } + // Attempt to queue the specified query + inst.value->m_Handle->mQueue.push_back(sql); + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + } + // Do we have enough values to call the format function? + else if (top > 2) + { + SStr sql = NULL; + SQInteger len = 0; + // Attempt to generate the specified string format + SQRESULT ret = sqstd_format(vm, 3, &len, &sql); + // Did the format failed? + if (SQ_FAILED(ret)) + return ret; + // Is there even a query to queue? + else if (IsQueryEmpty(sql)) + return sq_throwerror(vm,"No query to queue"); + // Attempt to queue the specified query + inst.value->m_Handle->mQueue.push_back(sql); + } + // All methods of retrieving the message value failed + else + return sq_throwerror(vm, "Unable to extract the query string"); + // This function does not return a value + return 0; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Connection::QueryF(HSQUIRRELVM vm) +{ + const Int32 top = sq_gettop(vm); + // Do we even have enough arguments? + if (top <= 1) + return sq_throwerror(vm, "Missing the query string"); + // Obtain the connection instance + Var< Connection * > inst(vm, 1); + // Do we have a valid connection instance? + if (!inst.value) + return sq_throwerror(vm, "Invalid connection instance"); + // Do we have a valid connection reference? + else if (!inst.value->m_Handle) + return sq_throwerror(vm, "Invalid SQLite connection reference"); + // Is the specified message value a string or something convertible to string? + else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1)))) + { + CCStr sql = NULL; + // Attempt to retrieve the string from the stack + if (SQ_FAILED(sq_getstring(vm, -1, &sql))) + { + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // Now we can throw the error message + return sq_throwerror(vm, "Unable to retrieve the query"); + } + // Attempt to create a statement with the specified query + ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql)); + // If the value was converted to a string then pop the string + sq_pop(vm, sq_gettop(vm) - top); + // See if any errors occured + if (Sqrat::Error::Occurred(vm)) + { + // Obtain the error message from sqrat + String msg = Sqrat::Error::Message(vm); + // Throw the error message further down the line + return sq_throwerror(vm, msg.c_str()); + } + } + // Do we have enough values to call the format function? + else if (top > 2) + { + SStr sql = NULL; + SQInteger len = 0; + // Attempt to generate the specified string format + SQRESULT ret = sqstd_format(vm, 3, &len, &sql); + // Did the format failed? + if (SQ_FAILED(ret)) + return ret; + // Attempt to create a statement with the specified query + ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql)); + // See if any errors occured + if (Sqrat::Error::Occurred(vm)) + { + // Obtain the error message from sqrat + String msg = Sqrat::Error::Message(vm); + // Throw the error message further down the line + return sq_throwerror(vm, msg.c_str()); + } + } + // All methods of retrieving the message value failed + else + return sq_throwerror(vm, "Unable to extract the query string"); + // At this point we should have a return value on the stack + return 1; +} + +} // Namespace:: SqMod diff --git a/modules/sqlite/Connection.hpp b/modules/sqlite/Connection.hpp new file mode 100644 index 00000000..d43c27ad --- /dev/null +++ b/modules/sqlite/Connection.hpp @@ -0,0 +1,578 @@ +#ifndef _SQSQLITE_CONNECTION_HPP_ +#define _SQSQLITE_CONNECTION_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" +#include "SqAPI.h" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Class used to manage a connection to an SQLite database. +*/ +class Connection +{ +protected: + + /* -------------------------------------------------------------------------------------------- + * Create the database connection resource. + */ + void Create(CSStr name, Int32 flags, CSStr vfs); + + /* -------------------------------------------------------------------------------------------- + * Validate the document reference and throw an error if invalid. + */ + bool Validate() const; + +private: + + // -------------------------------------------------------------------------------------------- + ConnHnd m_Handle; /* The handle to the managed database connection resource. */ + +public: + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + Connection(); + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + Connection(CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + Connection(CSStr name, Int32 flags); + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + Connection(CSStr name, Int32 flags, CSStr vfs); + + /* -------------------------------------------------------------------------------------------- + * Direct handle constructor. + */ + Connection(const ConnHnd & h) + : m_Handle(h) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Connection(const Connection & o) + : m_Handle(o.m_Handle) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~Connection() + { + /* Let the reference manager destroy the connection when necessary. */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Connection & operator = (const Connection & o) + { + m_Handle = o.m_Handle; + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison between two connections. + */ + bool operator == (const Connection & o) const + { + return (m_Handle == o.m_Handle); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison between two connections. + */ + bool operator != (const Connection & o) const + { + return (m_Handle != o.m_Handle); + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the raw connection handle. + */ + operator sqlite3 * () + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the raw connection handle. + */ + operator sqlite3 * () const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to compare two instances of this type. + */ + Int32 Cmp(const Connection & o) const + { + if (m_Handle == m_Handle) + return 0; + else if (m_Handle.m_Hnd > o.m_Handle.m_Hnd) + return 1; + else + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + CSStr ToString() const + { + // Validate the handle + if (Validate()) + return m_Handle->mName.c_str(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this connection is valid. + */ + bool IsValid() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated connection handle. + */ + const ConnHnd & GetHandle() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to this connection handle. + */ + Uint32 GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Release the reference to the associated database connection. + */ + void Release() + { + m_Handle.Drop(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the last received status code. + */ + Int32 GetStatus() const + { + // Validate the handle + if (Validate()) + return m_Handle->mStatus; + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the flags used to create this database connection. + */ + Int32 GetFlags() const + { + // Validate the handle + if (Validate()) + return m_Handle->mFlags; + // Request failed + return 0; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the name used to create this database connection. + */ + CSStr GetName() const + { + // Validate the handle + if (Validate()) + return m_Handle->mName.c_str(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the virtual file system used to create this database connection. + */ + CSStr GetVFS() const + { + // Validate the handle + if (Validate()) + return m_Handle->mVFS.c_str(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 GetErrorCode() const + { + if (Validate()) + return m_Handle.ErrNo(); + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 GetExtendedErrorCode() const + { + if (Validate()) + return m_Handle.ExErrNo(); + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CSStr GetErrStr() const + { + if (Validate()) + return m_Handle.ErrStr(); + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CSStr GetErrMsg() const + { + if (Validate()) + return m_Handle.ErrMsg(); + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + void Open(CSStr name) + { + if (m_Handle.m_Hnd) + m_Handle->Create(name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + void Open(CSStr name, Int32 flags) + { + if (m_Handle.m_Hnd) + m_Handle->Create(name, flags, NULL); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + void Open(CSStr name, Int32 flags, CSStr vfs) + { + if (m_Handle.m_Hnd) + m_Handle->Create(name, flags, vfs); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to execute the specified query. + */ + Int32 Exec(CSStr str); + + /* -------------------------------------------------------------------------------------------- + * Attempt to queue the specified query. + */ + void Queue(CSStr str); + + /* -------------------------------------------------------------------------------------------- + * Attempt to create a statement from the specified query. + */ + Statement Query(CSStr str) const; + + /* -------------------------------------------------------------------------------------------- + * See if the database connection was opened in read-only mode. + */ + bool IsReadOnly() const; + + /* -------------------------------------------------------------------------------------------- + * Shortcut to test if a table exists. + */ + bool TableExists(CCStr name) const; + + /* -------------------------------------------------------------------------------------------- + * See if the database connection is or is not in auto-commit mode. + */ + bool GetAutoCommit() const + { + // Request failed + if (Validate()) + return sqlite3_get_autocommit(m_Handle); + // Request failed + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Get the row-id of the most recent successful INSERT into the database from the current connection. + */ + Object GetLastInsertRowID() const; + + /* -------------------------------------------------------------------------------------------- + * Returns the number of database rows that were changed, inserted or deleted + * by the most recently completed SQL statement. + */ + Int32 GetChanges() const + { + // Validate the handle + if (Validate()) + return sqlite3_changes(m_Handle); + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Returns the total number of row changes caused by INSERT, UPDATE or DELETE statements + * since the database connection was opened. + */ + Int32 GetTotalChanges() const + { + // Validate the handle + if (Validate()) + return sqlite3_total_changes(m_Handle); + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * See if this database connection has tracing enabled. + */ + bool GetTracing() const + { + // Validate the handle + if (Validate()) + return m_Handle->mTrace; + // Request failed + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Activate or deactivate tracing on this database connection. + */ + void SetTracing(bool toggle) + { + // Validate the handle and check whether changes are necessary + if (!Validate() || m_Handle->mTrace == toggle) + return; /* No point in proceeding */ + // Do we have to disable it? + else if (m_Handle->mTrace) + sqlite3_trace(m_Handle, NULL, NULL); + // Go ahead and enable tracing + else + sqlite3_trace(m_Handle, &Connection::TraceOutput, NULL); + } + + /* -------------------------------------------------------------------------------------------- + * See if this database connection has profiling enabled. + */ + bool GetProfiling() const + { + // Validate the handle + if (Validate()) + return m_Handle->mProfile; + // Request failed + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Activate or deactivate profiling on this database connection. + */ + void SetProfiling(bool toggle) + { + // Validate the handle and check whether changes are necessary + if (!Validate() || m_Handle->mProfile == toggle) + return; /* No point in proceeding */ + // Do we have to disable it? + else if (m_Handle->mProfile) + sqlite3_profile(m_Handle, NULL, NULL); + // Go ahead and enable profiling + else + sqlite3_profile(m_Handle, &Connection::ProfileOutput, NULL); + } + + /* -------------------------------------------------------------------------------------------- + * Set a busy handler that sleeps for a specified amount of time when a table is locked. + */ + void SetBusyTimeout(Int32 millis); + + /* -------------------------------------------------------------------------------------------- + * Causes any pending database operation to abort and return at its earliest opportunity. + */ + void InterruptOperation() const + { + // Validate the handle + if (Validate()) + sqlite3_interrupt(m_Handle); + } + + /* -------------------------------------------------------------------------------------------- + * Attempts to free as much heap memory as possible from the database connection. + */ + void ReleaseMemory() const + { + // Validate the handle + if (Validate()) + sqlite3_db_release_memory(m_Handle); + } + + /* -------------------------------------------------------------------------------------------- + * Move the whole database into memory. + */ + Connection CopyToMemory(); + + /* -------------------------------------------------------------------------------------------- + * Takes a snapshot of a database which is located in memory and saves it to a database file. + */ + void CopyToDatabase(Connection & db); + + /* -------------------------------------------------------------------------------------------- + * Returns internal runtime status information associated with the current database connection. + */ + Int32 GetInfo(Int32 operation) + { + return GetInfo(operation, false, false); + } + + /* -------------------------------------------------------------------------------------------- + * Returns internal runtime status information associated with the current database connection. + */ + Int32 GetInfo(Int32 operation, bool highwater = false) + { + return GetInfo(operation, highwater, false); + } + + /* -------------------------------------------------------------------------------------------- + * Returns internal runtime status information associated with the current database connection. + */ + Int32 GetInfo(Int32 operation, bool highwater = false, bool reset = false); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of queries in the queue. + */ + Uint32 QueueSize() const + { + // Validate the handle + if (Validate()) + return (Uint32)m_Handle->mQueue.size(); + // Request failed + return 0; + } + + /* -------------------------------------------------------------------------------------------- + * Remove all queries from the queue without executing them. + */ + void ClearQueue() const + { + // Validate the handle + if (Validate()) + m_Handle->mQueue.clear(); + } + + /* -------------------------------------------------------------------------------------------- + * Remove the last query from the queue. + */ + void PopQueue() const + { + // Validate the handle + if (Validate() && !m_Handle->mQueue.empty()) + m_Handle->mQueue.pop_back(); + } + + /* -------------------------------------------------------------------------------------------- + * Flush all queries from the queue. + */ + Int32 Flush() + { + // Validate the handle + if (Validate()) + return Flush(m_Handle->mQueue.size()); + // Request failed + return 0; + } + + /* -------------------------------------------------------------------------------------------- + * Flush a specific amount of queries from the queue. + */ + Int32 Flush(Uint32 num); + + /* -------------------------------------------------------------------------------------------- + * Flush a specific amount of queries from the queue. + */ + Int32 Flush(Uint32 num, Object & env, Function & func); + + /* -------------------------------------------------------------------------------------------- + * Attempt to execute the specified query. + */ + static SQInteger ExecF(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * Attempt to queue the specified query. + */ + static SQInteger QueueF(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * Attempt to create a statement from the specified query. + */ + static SQInteger QueryF(HSQUIRRELVM vm); + +protected: + + /* -------------------------------------------------------------------------------------------- + * Callback function for ActivateTracing() + */ + static void TraceOutput(void * ptr, const char * sql); + + /* -------------------------------------------------------------------------------------------- + * Callback function for ActivateProfiling() + */ + static void ProfileOutput(void * ptr, const char * sql, sqlite3_uint64 time); + + /* -------------------------------------------------------------------------------------------- + * Build and modify the structure of tables and other objects in the memory database. + */ + static int ProcessDDLRow(void * db, int columns_count, char ** values, char ** columns); + + /* -------------------------------------------------------------------------------------------- + * Insert all data from the origin database into the memory database. + */ + static int ProcessDMLRow(void * db, int columns_count, char ** values, char ** columns); + + /* -------------------------------------------------------------------------------------------- + * Takes and saves a snapshot of the memory database in a file. + */ + void TakeSnapshot(ConnHnd & destination); +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_CONNECTION_HPP_ diff --git a/modules/sqlite/Module.cpp b/modules/sqlite/Module.cpp new file mode 100644 index 00000000..c34902f3 --- /dev/null +++ b/modules/sqlite/Module.cpp @@ -0,0 +1,813 @@ +// -------------------------------------------------------------------------------------------- +#include "Module.hpp" +#include "Common.hpp" +#include "Connection.hpp" +#include "Statement.hpp" +#include "Column.hpp" + +// -------------------------------------------------------------------------------------------- +#include + +// -------------------------------------------------------------------------------------------- +#include +#include +#include + +// -------------------------------------------------------------------------------------------- +#if defined(WIN32) || defined(_WIN32) + #include +#endif + +namespace SqMod { + +// -------------------------------------------------------------------------------------------- +PluginFuncs* _Func = NULL; +PluginCallbacks* _Clbk = NULL; +PluginInfo* _Info = NULL; + +// -------------------------------------------------------------------------------------------- +HSQAPI _SqAPI = NULL; +HSQEXPORTS _SqMod = NULL; +HSQUIRRELVM _SqVM = NULL; + +/* ------------------------------------------------------------------------------------------------ + * Bind speciffic functions to certain server events. +*/ +void BindCallbacks(); + +/* ------------------------------------------------------------------------------------------------ + * Undo changes made with 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. +*/ +void OnSquirrelInitialize() +{ + // Attempt to import the plugin API exported by the host plugin + _SqMod = sq_api_import(_Func); + // Did we failed to obtain the plugin exports? + if(!_SqMod) + OutputError("Failed to attach [%s] on host plugin.", SQSQLITE_NAME); + else + { + // Obtain the Squirrel API + _SqAPI = _SqMod->GetSquirrelAPI(); + // Expand the Squirrel API into global functions + sq_api_expand(_SqAPI); + } +} + +/* -------------------------------------------------------------------------------------------- + * Load the module on the virtual machine provided by the host module. +*/ +void OnSquirrelLoad() +{ + // Make sure that we have a valid plugin API + if (!_SqMod) + return; /* Unable to proceed. */ + // Obtain the Squirrel API and VM + _SqVM = _SqMod->GetSquirrelVM(); + // Make sure that a valid virtual machine exists + if (!_SqVM) + return; /* Unable to proceed. */ + // Set this as the default database + DefaultVM::Set(_SqVM); + // Register the module API + RegisterAPI(_SqVM); + // Notify about the current status + 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 the current database (if any) + DefaultVM::Set(NULL); +} + +/* -------------------------------------------------------------------------------------------- + * 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 = strtol(ver, NULL, 10); + // Check against version mismatch + if (vernum == SQMOD_API_VER) + return true; + // Log the incident + OutputError("API version mismatch on %s", SQSQLITE_NAME); + OutputMessage("=> Requested: %ld Have: %ld", vernum, SQMOD_API_VER); + // Invoker should not attempt to communicate through the module API + return false; +} + +/* -------------------------------------------------------------------------------------------- + * React to command sent by other plugins. +*/ +static int OnInternalCommand(unsigned int type, const char * text) +{ + switch(type) + { + case SQMOD_INITIALIZE_CMD: + if (CheckAPIVer(text)) + OnSquirrelInitialize(); + break; + case SQMOD_LOAD_CMD: + OnSquirrelLoad(); + break; + case SQMOD_TERMINATE_CMD: + OnSquirrelTerminate(); + break; + default: break; + } + return 1; +} + +/* -------------------------------------------------------------------------------------------- + * The server was initialized and this plugin was loaded successfully. +*/ +static int OnInitServer() +{ + return 1; +} + +static void OnShutdownServer(void) +{ + // The server may still send callbacks + UnbindCallbacks(); +} + +// ------------------------------------------------------------------------------------------------ +void BindCallbacks() +{ + _Clbk->OnInitServer = OnInitServer; + _Clbk->OnInternalCommand = OnInternalCommand; + _Clbk->OnShutdownServer = OnShutdownServer; +} + +// ------------------------------------------------------------------------------------------------ +void UnbindCallbacks() +{ + _Clbk->OnInitServer = NULL; + _Clbk->OnInternalCommand = NULL; + _Clbk->OnShutdownServer = NULL; +} + +// -------------------------------------------------------------------------------------------- +void RegisterAPI(HSQUIRRELVM vm) +{ + Table sqlns(vm); + + sqlns.Bind(_SC("Connection"), Class< Connection >(vm, _SC("SqSQLiteConnection")) + /* Constructors */ + .Ctor() + .Ctor< CCStr >() + .Ctor< CCStr, Int32 >() + .Ctor< CCStr, Int32, CCStr >() + /* Metamethods */ + .Func(_SC("_cmp"), &Connection::Cmp) + .SquirrelFunc(_SC("_typename"), &Connection::Typename) + .Func(_SC("_tostring"), &Connection::ToString) + /* Properties */ + .Prop(_SC("Valid"), &Connection::IsValid) + .Prop(_SC("Refs"), &Connection::GetRefCount) + .Prop(_SC("Connected"), &Connection::IsValid) + .Prop(_SC("Status"), &Connection::GetStatus) + .Prop(_SC("Flags"), &Connection::GetFlags) + .Prop(_SC("Name"), &Connection::GetName) + .Prop(_SC("VFS"), &Connection::GetVFS) + .Prop(_SC("ErrCode"), &Connection::GetErrorCode) + .Prop(_SC("ExErrCode"), &Connection::GetExtendedErrorCode) + .Prop(_SC("ExtendedErrCode"), &Connection::GetExtendedErrorCode) + .Prop(_SC("ErrStr"), &Connection::GetErrStr) + .Prop(_SC("ErrMsg"), &Connection::GetErrMsg) + .Prop(_SC("ReadOnly"), &Connection::IsReadOnly) + .Prop(_SC("Autocommit"), &Connection::GetAutoCommit) + .Prop(_SC("LastInsertRowId"), &Connection::GetLastInsertRowID) + .Prop(_SC("Changes"), &Connection::GetChanges) + .Prop(_SC("TotalChanges"), &Connection::GetTotalChanges) + .Prop(_SC("Trace"), &Connection::GetTracing, &Connection::SetTracing) + .Prop(_SC("Profile"), &Connection::GetProfiling, &Connection::SetProfiling) + .Prop(_SC("BusyTimeout"), (Int32 (Connection::*)(void) const)(NULL), &Connection::SetBusyTimeout) + .Prop(_SC("QueueSize"), &Connection::QueueSize) + /* Functions */ + .Func(_SC("Release"), &Connection::Release) + .Overload< void (Connection::*)(CSStr) >(_SC("Open"), &Connection::Open) + .Overload< void (Connection::*)(CSStr, Int32) >(_SC("Open"), &Connection::Open) + .Overload< void (Connection::*)(CSStr, Int32, CSStr) >(_SC("Open"), &Connection::Open) + .Func(_SC("Exec"), &Connection::Exec) + .Func(_SC("Queue"), &Connection::Queue) + .Func(_SC("Query"), &Connection::Query) + .Func(_SC("TableExists"), &Connection::TableExists) + .Func(_SC("InterruptOperation"), &Connection::InterruptOperation) + .Func(_SC("ReleaseMemory"), &Connection::ReleaseMemory) + .Func(_SC("CopyToMemory"), &Connection::CopyToMemory) + .Func(_SC("CopyToDatabase"), &Connection::CopyToDatabase) + .Overload< Int32 (Connection::*)(Int32) >(_SC("GetInfo"), &Connection::GetInfo) + .Overload< Int32 (Connection::*)(Int32, bool) >(_SC("GetInfo"), &Connection::GetInfo) + .Overload< Int32 (Connection::*)(Int32, bool, bool) >(_SC("GetInfo"), &Connection::GetInfo) + .Func(_SC("ClearQueue"), &Connection::ClearQueue) + .Func(_SC("PopQueue"), &Connection::PopQueue) + .Overload< Int32 (Connection::*)(void) >(_SC("Flush"), &Connection::Flush) + .Overload< Int32 (Connection::*)(Uint32) >(_SC("Flush"), &Connection::Flush) + .Overload< Int32 (Connection::*)(Uint32, Object &, Function &) >(_SC("Flush"), &Connection::Flush) + .SquirrelFunc(_SC("ExecF"), &Connection::ExecF) + .SquirrelFunc(_SC("QueueF"), &Connection::QueueF) + .SquirrelFunc(_SC("QueryF"), &Connection::QueryF) + ); + + sqlns.Bind(_SC("Statement"), Class< Statement >(vm, _SC("SqSQLiteStatement")) + /* Constructors */ + .Ctor() + .Ctor< const Connection &, CCStr >() + .Ctor< const Statement & >() + /* Metamethods */ + .Func(_SC("_cmp"), &Statement::Cmp) + .SquirrelFunc(_SC("_typename"), &Statement::Typename) + .Func(_SC("_tostring"), &Statement::ToString) + /* Properties */ + .Prop(_SC("Valid"), &Statement::IsValid) + .Prop(_SC("Refs"), &Statement::GetRefCount) + .Prop(_SC("Connection"), &Statement::GetConnection) + .Prop(_SC("Prepared"), &Statement::IsValid) + .Prop(_SC("Status"), &Statement::GetStatus) + .Prop(_SC("ErrCode"), &Statement::GetErrorCode) + .Prop(_SC("ExErrCode"), &Statement::GetExtendedErrorCode) + .Prop(_SC("ExtendedErrCode"), &Statement::GetExtendedErrorCode) + .Prop(_SC("ErrStr"), &Statement::GetErrStr) + .Prop(_SC("ErrMsg"), &Statement::GetErrMsg) + .Prop(_SC("Columns"), &Statement::GetColumns) + .Prop(_SC("Query"), &Statement::GetQuery) + .Prop(_SC("Good"), &Statement::GetGood) + .Prop(_SC("Done"), &Statement::GetDone) + /* Functions */ + .Func(_SC("Release"), &Statement::Release) + .Func(_SC("Reset"), &Statement::Reset) + .Func(_SC("Clear"), &Statement::Clear) + .Func(_SC("Exec"), &Statement::Exec) + .Func(_SC("Step"), &Statement::Step) + .Func(_SC("IBindA"), &Statement::IndexBindA) + .Func(_SC("IBindI"), &Statement::IndexBindI) + .Func(_SC("IBindL"), &Statement::IndexBindL) + .Func(_SC("IBindV"), &Statement::IndexBindV) + .Func(_SC("IBindF"), &Statement::IndexBindF) + .Func(_SC("IBindS"), &Statement::IndexBindS) + .Func(_SC("IBindB"), &Statement::IndexBindB) + .Func(_SC("IBindN"), &Statement::IndexBindN) + .Func(_SC("NBindT"), &Statement::NameBindT) + .Func(_SC("NBindI"), &Statement::NameBindI) + .Func(_SC("NBindL"), &Statement::NameBindL) + .Func(_SC("NBindV"), &Statement::NameBindV) + .Func(_SC("NBindF"), &Statement::NameBindF) + .Func(_SC("NBindS"), &Statement::NameBindS) + .Func(_SC("NBindB"), &Statement::NameBindB) + .Func(_SC("NBindN"), &Statement::NameBindN) + .Func(_SC("IBind"), &Statement::IndexBind) + .Func(_SC("NBind"), &Statement::NameBind) + .Func(_SC("FetchI"), &Statement::FetchColumnIndex) + .Func(_SC("FetchN"), &Statement::FetchColumnName) + .Overload< Array (Statement::*)(void) const >(_SC("FetchA"), &Statement::FetchArray) + .Overload< Array (Statement::*)(Int32) const >(_SC("FetchA"), &Statement::FetchArray) + .Overload< Array (Statement::*)(Int32, Int32) const >(_SC("FetchA"), &Statement::FetchArray) + .Overload< Table (Statement::*)(void) const >(_SC("FetchT"), &Statement::FetchTable) + .Overload< Table (Statement::*)(Int32) const >(_SC("FetchT"), &Statement::FetchTable) + .Overload< Table (Statement::*)(Int32, Int32) const >(_SC("FetchT"), &Statement::FetchTable) + .Func(_SC("CheckIndex"), &Statement::CheckIndex) + .Func(_SC("IsColumnNull"), &Statement::IsColumnNull) + .Func(_SC("ColumnIndex"), &Statement::GetColumnIndex) + .Func(_SC("ColumnName"), &Statement::GetColumnName) + .Func(_SC("ColumnOriginName"), &Statement::GetColumnOriginName) + .Func(_SC("ColumnType"), &Statement::GetColumnType) + .Func(_SC("ColumnBytes"), &Statement::GetColumnBytes) + .Func(_SC("GetI"), &Statement::GetColumnByIndex) + .Func(_SC("GetN"), &Statement::GetColumnByName) + ); + + sqlns.Bind(_SC("Column"), Class< Column >(vm, _SC("SqSQLiteColumn")) + /* Constructors */ + .Ctor() + .Ctor< const Column & >() + /* Metamethods */ + .Func(_SC("_cmp"), &Column::Cmp) + .SquirrelFunc(_SC("_typename"), &Column::Typename) + .Func(_SC("_tostring"), &Column::ToString) + /* Properties */ + .Prop(_SC("Valid"), &Column::IsValid) + .Prop(_SC("Refs"), &Column::GetRefCount) + .Prop(_SC("Index"), &Column::GetIndex) + .Prop(_SC("Statement"), &Column::GetNumber) + .Prop(_SC("Connection"), &Column::GetConnection) + .Prop(_SC("Number"), &Column::GetNumber) + .Prop(_SC("Integer"), &Column::GetInteger) + .Prop(_SC("Float"), &Column::GetFloat) + .Prop(_SC("Long"), &Column::GetLong) + .Prop(_SC("String"), &Column::GetString) + .Prop(_SC("Boolean"), &Column::GetBoolean) + .Prop(_SC("Blob"), &Column::GetBlob) + .Prop(_SC("Char"), &Column::GetChar) + .Prop(_SC("IsNull"), &Column::IsNull) + .Prop(_SC("Name"), &Column::GetName) + .Prop(_SC("OriginName"), &Column::GetOriginName) + .Prop(_SC("Type"), &Column::GetType) + .Prop(_SC("Bytes"), &Column::GetBytes) + /* Functions */ + .Func(_SC("Release"), &Column::Release) + ); + + sqlns.Func(_SC("SetSoftHeapLimit"), &SetSoftHeapLimit); + sqlns.Func(_SC("ReleaseMemory"), &ReleaseMemory); + sqlns.Func(_SC("MemoryUsage"), &GetMemoryUsage); + sqlns.Func(_SC("EscapeString"), &EscapeString); + sqlns.Func(_SC("EscapeStringEx"), &EscapeStringEx); + sqlns.Func(_SC("Escape"), &EscapeString); + sqlns.Func(_SC("EscapeEx"), &EscapeStringEx); + sqlns.Func(_SC("ArrayToQueryColumns"), &ArrayToQueryColumns); + sqlns.Func(_SC("TableToQueryColumns"), &TableToQueryColumns); + + RootTable(vm).Bind(_SC("SQLite"), sqlns); + +/* + ConstTable(vm).Enum(_SC("ESQLite"), Enumeration(vm) + .Const(_SC("ABORT"), SQLITE_ABORT) + .Const(_SC("ABORT_ROLLBACK"), SQLITE_ABORT_ROLLBACK) + .Const(_SC("ACCESS_EXISTS"), SQLITE_ACCESS_EXISTS) + .Const(_SC("ACCESS_READ"), SQLITE_ACCESS_READ) + .Const(_SC("ACCESS_READWRITE"), SQLITE_ACCESS_READWRITE) + .Const(_SC("ALTER_TABLE"), SQLITE_ALTER_TABLE) + .Const(_SC("ANALYZE"), SQLITE_ANALYZE) + .Const(_SC("ANY"), SQLITE_ANY) + .Const(_SC("ATTACH"), SQLITE_ATTACH) + .Const(_SC("AUTH"), SQLITE_AUTH) + .Const(_SC("AUTH_USER"), SQLITE_AUTH_USER) + .Const(_SC("BLOB"), SQLITE_BLOB) + .Const(_SC("BUSY"), SQLITE_BUSY) + .Const(_SC("BUSY_RECOVERY"), SQLITE_BUSY_RECOVERY) + .Const(_SC("BUSY_SNAPSHOT"), SQLITE_BUSY_SNAPSHOT) + .Const(_SC("CANTOPEN"), SQLITE_CANTOPEN) + .Const(_SC("CANTOPEN_CONVPATH"), SQLITE_CANTOPEN_CONVPATH) + .Const(_SC("CANTOPEN_FULLPATH"), SQLITE_CANTOPEN_FULLPATH) + .Const(_SC("CANTOPEN_ISDIR"), SQLITE_CANTOPEN_ISDIR) + .Const(_SC("CANTOPEN_NOTEMPDIR"), SQLITE_CANTOPEN_NOTEMPDIR) + .Const(_SC("CHECKPOINT_FULL"), SQLITE_CHECKPOINT_FULL) + .Const(_SC("CHECKPOINT_PASSIVE"), SQLITE_CHECKPOINT_PASSIVE) + .Const(_SC("CHECKPOINT_RESTART"), SQLITE_CHECKPOINT_RESTART) + .Const(_SC("CHECKPOINT_TRUNCATE"), SQLITE_CHECKPOINT_TRUNCATE) + .Const(_SC("CONFIG_COVERING_INDEX_SCAN"), SQLITE_CONFIG_COVERING_INDEX_SCAN) + .Const(_SC("CONFIG_GETMALLOC"), SQLITE_CONFIG_GETMALLOC) + .Const(_SC("CONFIG_GETMUTEX"), SQLITE_CONFIG_GETMUTEX) + .Const(_SC("CONFIG_GETPCACHE"), SQLITE_CONFIG_GETPCACHE) + .Const(_SC("CONFIG_GETPCACHE2"), SQLITE_CONFIG_GETPCACHE2) + .Const(_SC("CONFIG_HEAP"), SQLITE_CONFIG_HEAP) + .Const(_SC("CONFIG_LOG"), SQLITE_CONFIG_LOG) + .Const(_SC("CONFIG_LOOKASIDE"), SQLITE_CONFIG_LOOKASIDE) + .Const(_SC("CONFIG_MALLOC"), SQLITE_CONFIG_MALLOC) + .Const(_SC("CONFIG_MEMSTATUS"), SQLITE_CONFIG_MEMSTATUS) + .Const(_SC("CONFIG_MMAP_SIZE"), SQLITE_CONFIG_MMAP_SIZE) + .Const(_SC("CONFIG_MULTITHREAD"), SQLITE_CONFIG_MULTITHREAD) + .Const(_SC("CONFIG_MUTEX"), SQLITE_CONFIG_MUTEX) + .Const(_SC("CONFIG_PAGECACHE"), SQLITE_CONFIG_PAGECACHE) + .Const(_SC("CONFIG_PCACHE"), SQLITE_CONFIG_PCACHE) + .Const(_SC("CONFIG_PCACHE2"), SQLITE_CONFIG_PCACHE2) + .Const(_SC("CONFIG_PCACHE_HDRSZ"), SQLITE_CONFIG_PCACHE_HDRSZ) + .Const(_SC("CONFIG_PMASZ"), SQLITE_CONFIG_PMASZ) + .Const(_SC("CONFIG_SCRATCH"), SQLITE_CONFIG_SCRATCH) + .Const(_SC("CONFIG_SERIALIZED"), SQLITE_CONFIG_SERIALIZED) + .Const(_SC("CONFIG_SINGLETHREAD"), SQLITE_CONFIG_SINGLETHREAD) + .Const(_SC("CONFIG_SQLLOG"), SQLITE_CONFIG_SQLLOG) + .Const(_SC("CONFIG_URI"), SQLITE_CONFIG_URI) + .Const(_SC("CONFIG_WIN32_HEAPSIZE"), SQLITE_CONFIG_WIN32_HEAPSIZE) + .Const(_SC("CONSTRAINT"), SQLITE_CONSTRAINT) + .Const(_SC("CONSTRAINT_CHECK"), SQLITE_CONSTRAINT_CHECK) + .Const(_SC("CONSTRAINT_COMMITHOOK"), SQLITE_CONSTRAINT_COMMITHOOK) + .Const(_SC("CONSTRAINT_FOREIGNKEY"), SQLITE_CONSTRAINT_FOREIGNKEY) + .Const(_SC("CONSTRAINT_FUNCTION"), SQLITE_CONSTRAINT_FUNCTION) + .Const(_SC("CONSTRAINT_NOTNULL"), SQLITE_CONSTRAINT_NOTNULL) + .Const(_SC("CONSTRAINT_PRIMARYKEY"), SQLITE_CONSTRAINT_PRIMARYKEY) + .Const(_SC("CONSTRAINT_ROWID"), SQLITE_CONSTRAINT_ROWID) + .Const(_SC("CONSTRAINT_TRIGGER"), SQLITE_CONSTRAINT_TRIGGER) + .Const(_SC("CONSTRAINT_UNIQUE"), SQLITE_CONSTRAINT_UNIQUE) + .Const(_SC("CONSTRAINT_VTAB"), SQLITE_CONSTRAINT_VTAB) + .Const(_SC("COPY"), SQLITE_COPY) + .Const(_SC("CORRUPT"), SQLITE_CORRUPT) + .Const(_SC("CORRUPT_VTAB"), SQLITE_CORRUPT_VTAB) + .Const(_SC("CREATE_INDEX"), SQLITE_CREATE_INDEX) + .Const(_SC("CREATE_TABLE"), SQLITE_CREATE_TABLE) + .Const(_SC("CREATE_TEMP_INDEX"), SQLITE_CREATE_TEMP_INDEX) + .Const(_SC("CREATE_TEMP_TABLE"), SQLITE_CREATE_TEMP_TABLE) + .Const(_SC("CREATE_TEMP_TRIGGER"), SQLITE_CREATE_TEMP_TRIGGER) + .Const(_SC("CREATE_TEMP_VIEW"), SQLITE_CREATE_TEMP_VIEW) + .Const(_SC("CREATE_TRIGGER"), SQLITE_CREATE_TRIGGER) + .Const(_SC("CREATE_VIEW"), SQLITE_CREATE_VIEW) + .Const(_SC("CREATE_VTABLE"), SQLITE_CREATE_VTABLE) + .Const(_SC("DBCONFIG_ENABLE_FKEY"), SQLITE_DBCONFIG_ENABLE_FKEY) + .Const(_SC("DBCONFIG_ENABLE_TRIGGER"), SQLITE_DBCONFIG_ENABLE_TRIGGER) + .Const(_SC("DBCONFIG_LOOKASIDE"), SQLITE_DBCONFIG_LOOKASIDE) + .Const(_SC("DBSTATUS_CACHE_HIT"), SQLITE_DBSTATUS_CACHE_HIT) + .Const(_SC("DBSTATUS_CACHE_MISS"), SQLITE_DBSTATUS_CACHE_MISS) + .Const(_SC("DBSTATUS_CACHE_USED"), SQLITE_DBSTATUS_CACHE_USED) + .Const(_SC("DBSTATUS_CACHE_WRITE"), SQLITE_DBSTATUS_CACHE_WRITE) + .Const(_SC("DBSTATUS_DEFERRED_FKS"), SQLITE_DBSTATUS_DEFERRED_FKS) + .Const(_SC("DBSTATUS_LOOKASIDE_HIT"), SQLITE_DBSTATUS_LOOKASIDE_HIT) + .Const(_SC("DBSTATUS_LOOKASIDE_MISS_FULL"), SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL) + .Const(_SC("DBSTATUS_LOOKASIDE_MISS_SIZE"), SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE) + .Const(_SC("DBSTATUS_LOOKASIDE_USED"), SQLITE_DBSTATUS_LOOKASIDE_USED) + .Const(_SC("DBSTATUS_MAX"), SQLITE_DBSTATUS_MAX) + .Const(_SC("DBSTATUS_SCHEMA_USED"), SQLITE_DBSTATUS_SCHEMA_USED) + .Const(_SC("DBSTATUS_STMT_USED"), SQLITE_DBSTATUS_STMT_USED) + .Const(_SC("DELETE"), SQLITE_DELETE) + .Const(_SC("DENY"), SQLITE_DENY) + .Const(_SC("DETACH"), SQLITE_DETACH) + .Const(_SC("DETERMINISTIC"), SQLITE_DETERMINISTIC) + .Const(_SC("DONE"), SQLITE_DONE) + .Const(_SC("DROP_INDEX"), SQLITE_DROP_INDEX) + .Const(_SC("DROP_TABLE"), SQLITE_DROP_TABLE) + .Const(_SC("DROP_TEMP_INDEX"), SQLITE_DROP_TEMP_INDEX) + .Const(_SC("DROP_TEMP_TABLE"), SQLITE_DROP_TEMP_TABLE) + .Const(_SC("DROP_TEMP_TRIGGER"), SQLITE_DROP_TEMP_TRIGGER) + .Const(_SC("DROP_TEMP_VIEW"), SQLITE_DROP_TEMP_VIEW) + .Const(_SC("DROP_TRIGGER"), SQLITE_DROP_TRIGGER) + .Const(_SC("DROP_VIEW"), SQLITE_DROP_VIEW) + .Const(_SC("DROP_VTABLE"), SQLITE_DROP_VTABLE) + .Const(_SC("EMPTY"), SQLITE_EMPTY) + .Const(_SC("ERROR"), SQLITE_ERROR) + .Const(_SC("FAIL"), SQLITE_FAIL) + .Const(_SC("FCNTL_BUSYHANDLER"), SQLITE_FCNTL_BUSYHANDLER) + .Const(_SC("FCNTL_CHUNK_SIZE"), SQLITE_FCNTL_CHUNK_SIZE) + .Const(_SC("FCNTL_COMMIT_PHASETWO"), SQLITE_FCNTL_COMMIT_PHASETWO) + .Const(_SC("FCNTL_FILE_POINTER"), SQLITE_FCNTL_FILE_POINTER) + .Const(_SC("FCNTL_GET_LOCKPROXYFILE"), SQLITE_FCNTL_GET_LOCKPROXYFILE) + .Const(_SC("FCNTL_HAS_MOVED"), SQLITE_FCNTL_HAS_MOVED) + .Const(_SC("FCNTL_LAST_ERRNO"), SQLITE_FCNTL_LAST_ERRNO) + .Const(_SC("FCNTL_LOCKSTATE"), SQLITE_FCNTL_LOCKSTATE) + .Const(_SC("FCNTL_MMAP_SIZE"), SQLITE_FCNTL_MMAP_SIZE) + .Const(_SC("FCNTL_OVERWRITE"), SQLITE_FCNTL_OVERWRITE) + .Const(_SC("FCNTL_PERSIST_WAL"), SQLITE_FCNTL_PERSIST_WAL) + .Const(_SC("FCNTL_POWERSAFE_OVERWRITE"), SQLITE_FCNTL_POWERSAFE_OVERWRITE) + .Const(_SC("FCNTL_PRAGMA"), SQLITE_FCNTL_PRAGMA) + .Const(_SC("FCNTL_RBU"), SQLITE_FCNTL_RBU) + .Const(_SC("FCNTL_SET_LOCKPROXYFILE"), SQLITE_FCNTL_SET_LOCKPROXYFILE) + .Const(_SC("FCNTL_SIZE_HINT"), SQLITE_FCNTL_SIZE_HINT) + .Const(_SC("FCNTL_SYNC"), SQLITE_FCNTL_SYNC) + .Const(_SC("FCNTL_SYNC_OMITTED"), SQLITE_FCNTL_SYNC_OMITTED) + .Const(_SC("FCNTL_TEMPFILENAME"), SQLITE_FCNTL_TEMPFILENAME) + .Const(_SC("FCNTL_TRACE"), SQLITE_FCNTL_TRACE) + .Const(_SC("FCNTL_VFSNAME"), SQLITE_FCNTL_VFSNAME) + .Const(_SC("FCNTL_WAL_BLOCK"), SQLITE_FCNTL_WAL_BLOCK) + .Const(_SC("FCNTL_WIN32_AV_RETRY"), SQLITE_FCNTL_WIN32_AV_RETRY) + .Const(_SC("FCNTL_WIN32_SET_HANDLE"), SQLITE_FCNTL_WIN32_SET_HANDLE) + .Const(_SC("FCNTL_ZIPVFS"), SQLITE_FCNTL_ZIPVFS) + .Const(_SC("FLOAT"), SQLITE_FLOAT) + .Const(_SC("FORMAT"), SQLITE_FORMAT) + .Const(_SC("FULL"), SQLITE_FULL) + .Const(_SC("FUNCTION"), SQLITE_FUNCTION) + .Const(_SC("IGNORE"), SQLITE_IGNORE) + .Const(_SC("INDEX_CONSTRAINT_EQ"), SQLITE_INDEX_CONSTRAINT_EQ) + .Const(_SC("INDEX_CONSTRAINT_GE"), SQLITE_INDEX_CONSTRAINT_GE) + .Const(_SC("INDEX_CONSTRAINT_GT"), SQLITE_INDEX_CONSTRAINT_GT) + .Const(_SC("INDEX_CONSTRAINT_LE"), SQLITE_INDEX_CONSTRAINT_LE) + .Const(_SC("INDEX_CONSTRAINT_LT"), SQLITE_INDEX_CONSTRAINT_LT) + .Const(_SC("INDEX_CONSTRAINT_MATCH"), SQLITE_INDEX_CONSTRAINT_MATCH) + .Const(_SC("INDEX_SCAN_UNIQUE"), SQLITE_INDEX_SCAN_UNIQUE) + .Const(_SC("INSERT"), SQLITE_INSERT) + .Const(_SC("INTEGER"), SQLITE_INTEGER) + .Const(_SC("INTERNAL"), SQLITE_INTERNAL) + .Const(_SC("INTERRUPT"), SQLITE_INTERRUPT) + .Const(_SC("IOCAP_ATOMIC"), SQLITE_IOCAP_ATOMIC) + .Const(_SC("IOCAP_ATOMIC16K"), SQLITE_IOCAP_ATOMIC16K) + .Const(_SC("IOCAP_ATOMIC1K"), SQLITE_IOCAP_ATOMIC1K) + .Const(_SC("IOCAP_ATOMIC2K"), SQLITE_IOCAP_ATOMIC2K) + .Const(_SC("IOCAP_ATOMIC32K"), SQLITE_IOCAP_ATOMIC32K) + .Const(_SC("IOCAP_ATOMIC4K"), SQLITE_IOCAP_ATOMIC4K) + .Const(_SC("IOCAP_ATOMIC512"), SQLITE_IOCAP_ATOMIC512) + .Const(_SC("IOCAP_ATOMIC64K"), SQLITE_IOCAP_ATOMIC64K) + .Const(_SC("IOCAP_ATOMIC8K"), SQLITE_IOCAP_ATOMIC8K) + .Const(_SC("IOCAP_IMMUTABLE"), SQLITE_IOCAP_IMMUTABLE) + .Const(_SC("IOCAP_POWERSAFE_OVERWRITE"), SQLITE_IOCAP_POWERSAFE_OVERWRITE) + .Const(_SC("IOCAP_SAFE_APPEND"), SQLITE_IOCAP_SAFE_APPEND) + .Const(_SC("IOCAP_SEQUENTIAL"), SQLITE_IOCAP_SEQUENTIAL) + .Const(_SC("IOCAP_UNDELETABLE_WHEN_OPEN"), SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) + .Const(_SC("IOERR"), SQLITE_IOERR) + .Const(_SC("IOERR_ACCESS"), SQLITE_IOERR_ACCESS) + .Const(_SC("IOERR_BLOCKED"), SQLITE_IOERR_BLOCKED) + .Const(_SC("IOERR_CHECKRESERVEDLOCK"), SQLITE_IOERR_CHECKRESERVEDLOCK) + .Const(_SC("IOERR_CLOSE"), SQLITE_IOERR_CLOSE) + .Const(_SC("IOERR_CONVPATH"), SQLITE_IOERR_CONVPATH) + .Const(_SC("IOERR_DELETE"), SQLITE_IOERR_DELETE) + .Const(_SC("IOERR_DELETE_NOENT"), SQLITE_IOERR_DELETE_NOENT) + .Const(_SC("IOERR_DIR_CLOSE"), SQLITE_IOERR_DIR_CLOSE) + .Const(_SC("IOERR_DIR_FSYNC"), SQLITE_IOERR_DIR_FSYNC) + .Const(_SC("IOERR_FSTAT"), SQLITE_IOERR_FSTAT) + .Const(_SC("IOERR_FSYNC"), SQLITE_IOERR_FSYNC) + .Const(_SC("IOERR_GETTEMPPATH"), SQLITE_IOERR_GETTEMPPATH) + .Const(_SC("IOERR_LOCK"), SQLITE_IOERR_LOCK) + .Const(_SC("IOERR_MMAP"), SQLITE_IOERR_MMAP) + .Const(_SC("IOERR_NOMEM"), SQLITE_IOERR_NOMEM) + .Const(_SC("IOERR_RDLOCK"), SQLITE_IOERR_RDLOCK) + .Const(_SC("IOERR_READ"), SQLITE_IOERR_READ) + .Const(_SC("IOERR_SEEK"), SQLITE_IOERR_SEEK) + .Const(_SC("IOERR_SHMLOCK"), SQLITE_IOERR_SHMLOCK) + .Const(_SC("IOERR_SHMMAP"), SQLITE_IOERR_SHMMAP) + .Const(_SC("IOERR_SHMOPEN"), SQLITE_IOERR_SHMOPEN) + .Const(_SC("IOERR_SHMSIZE"), SQLITE_IOERR_SHMSIZE) + .Const(_SC("IOERR_SHORT_READ"), SQLITE_IOERR_SHORT_READ) + .Const(_SC("IOERR_TRUNCATE"), SQLITE_IOERR_TRUNCATE) + .Const(_SC("IOERR_UNLOCK"), SQLITE_IOERR_UNLOCK) + .Const(_SC("IOERR_VNODE"), SQLITE_IOERR_VNODE) + .Const(_SC("IOERR_WRITE"), SQLITE_IOERR_WRITE) + .Const(_SC("LIMIT_ATTACHED"), SQLITE_LIMIT_ATTACHED) + .Const(_SC("LIMIT_COLUMN"), SQLITE_LIMIT_COLUMN) + .Const(_SC("LIMIT_COMPOUND_SELECT"), SQLITE_LIMIT_COMPOUND_SELECT) + .Const(_SC("LIMIT_EXPR_DEPTH"), SQLITE_LIMIT_EXPR_DEPTH) + .Const(_SC("LIMIT_FUNCTION_ARG"), SQLITE_LIMIT_FUNCTION_ARG) + .Const(_SC("LIMIT_LENGTH"), SQLITE_LIMIT_LENGTH) + .Const(_SC("LIMIT_LIKE_PATTERN_LENGTH"), SQLITE_LIMIT_LIKE_PATTERN_LENGTH) + .Const(_SC("LIMIT_SQL_LENGTH"), SQLITE_LIMIT_SQL_LENGTH) + .Const(_SC("LIMIT_TRIGGER_DEPTH"), SQLITE_LIMIT_TRIGGER_DEPTH) + .Const(_SC("LIMIT_VARIABLE_NUMBER"), SQLITE_LIMIT_VARIABLE_NUMBER) + .Const(_SC("LIMIT_VDBE_OP"), SQLITE_LIMIT_VDBE_OP) + .Const(_SC("LIMIT_WORKER_THREADS"), SQLITE_LIMIT_WORKER_THREADS) + .Const(_SC("LOCKED"), SQLITE_LOCKED) + .Const(_SC("LOCKED_SHAREDCACHE"), SQLITE_LOCKED_SHAREDCACHE) + .Const(_SC("LOCK_EXCLUSIVE"), SQLITE_LOCK_EXCLUSIVE) + .Const(_SC("LOCK_NONE"), SQLITE_LOCK_NONE) + .Const(_SC("LOCK_PENDING"), SQLITE_LOCK_PENDING) + .Const(_SC("LOCK_RESERVED"), SQLITE_LOCK_RESERVED) + .Const(_SC("LOCK_SHARED"), SQLITE_LOCK_SHARED) + .Const(_SC("MISMATCH"), SQLITE_MISMATCH) + .Const(_SC("MISUSE"), SQLITE_MISUSE) + .Const(_SC("MUTEX_FAST"), SQLITE_MUTEX_FAST) + .Const(_SC("MUTEX_RECURSIVE"), SQLITE_MUTEX_RECURSIVE) + .Const(_SC("MUTEX_STATIC_APP1"), SQLITE_MUTEX_STATIC_APP1) + .Const(_SC("MUTEX_STATIC_APP2"), SQLITE_MUTEX_STATIC_APP2) + .Const(_SC("MUTEX_STATIC_APP3"), SQLITE_MUTEX_STATIC_APP3) + .Const(_SC("MUTEX_STATIC_LRU"), SQLITE_MUTEX_STATIC_LRU) + .Const(_SC("MUTEX_STATIC_LRU2"), SQLITE_MUTEX_STATIC_LRU2) + .Const(_SC("MUTEX_STATIC_MASTER"), SQLITE_MUTEX_STATIC_MASTER) + .Const(_SC("MUTEX_STATIC_MEM"), SQLITE_MUTEX_STATIC_MEM) + .Const(_SC("MUTEX_STATIC_MEM2"), SQLITE_MUTEX_STATIC_MEM2) + .Const(_SC("MUTEX_STATIC_OPEN"), SQLITE_MUTEX_STATIC_OPEN) + .Const(_SC("MUTEX_STATIC_PMEM"), SQLITE_MUTEX_STATIC_PMEM) + .Const(_SC("MUTEX_STATIC_PRNG"), SQLITE_MUTEX_STATIC_PRNG) + .Const(_SC("MUTEX_STATIC_VFS1"), SQLITE_MUTEX_STATIC_VFS1) + .Const(_SC("MUTEX_STATIC_VFS2"), SQLITE_MUTEX_STATIC_VFS2) + .Const(_SC("MUTEX_STATIC_VFS3"), SQLITE_MUTEX_STATIC_VFS3) + .Const(_SC("NOLFS"), SQLITE_NOLFS) + .Const(_SC("NOMEM"), SQLITE_NOMEM) + .Const(_SC("NOTADB"), SQLITE_NOTADB) + .Const(_SC("NOTFOUND"), SQLITE_NOTFOUND) + .Const(_SC("NOTICE"), SQLITE_NOTICE) + .Const(_SC("NOTICE_RECOVER_ROLLBACK"), SQLITE_NOTICE_RECOVER_ROLLBACK) + .Const(_SC("NOTICE_RECOVER_WAL"), SQLITE_NOTICE_RECOVER_WAL) + .Const(_SC("NULL"), SQLITE_NULL) + .Const(_SC("OK"), SQLITE_OK) + .Const(_SC("OPEN_AUTOPROXY"), SQLITE_OPEN_AUTOPROXY) + .Const(_SC("OPEN_CREATE"), SQLITE_OPEN_CREATE) + .Const(_SC("OPEN_DELETEONCLOSE"), SQLITE_OPEN_DELETEONCLOSE) + .Const(_SC("OPEN_EXCLUSIVE"), SQLITE_OPEN_EXCLUSIVE) + .Const(_SC("OPEN_FULLMUTEX"), SQLITE_OPEN_FULLMUTEX) + .Const(_SC("OPEN_MAIN_DB"), SQLITE_OPEN_MAIN_DB) + .Const(_SC("OPEN_MAIN_JOURNAL"), SQLITE_OPEN_MAIN_JOURNAL) + .Const(_SC("OPEN_MASTER_JOURNAL"), SQLITE_OPEN_MASTER_JOURNAL) + .Const(_SC("OPEN_MEMORY"), SQLITE_OPEN_MEMORY) + .Const(_SC("OPEN_NOMUTEX"), SQLITE_OPEN_NOMUTEX) + .Const(_SC("OPEN_PRIVATECACHE"), SQLITE_OPEN_PRIVATECACHE) + .Const(_SC("OPEN_READONLY"), SQLITE_OPEN_READONLY) + .Const(_SC("OPEN_READWRITE"), SQLITE_OPEN_READWRITE) + .Const(_SC("OPEN_SHAREDCACHE"), SQLITE_OPEN_SHAREDCACHE) + .Const(_SC("OPEN_SUBJOURNAL"), SQLITE_OPEN_SUBJOURNAL) + .Const(_SC("OPEN_TEMP_DB"), SQLITE_OPEN_TEMP_DB) + .Const(_SC("OPEN_TEMP_JOURNAL"), SQLITE_OPEN_TEMP_JOURNAL) + .Const(_SC("OPEN_TRANSIENT_DB"), SQLITE_OPEN_TRANSIENT_DB) + .Const(_SC("OPEN_URI"), SQLITE_OPEN_URI) + .Const(_SC("OPEN_WAL"), SQLITE_OPEN_WAL) + .Const(_SC("PERM"), SQLITE_PERM) + .Const(_SC("PRAGMA"), SQLITE_PRAGMA) + .Const(_SC("PROTOCOL"), SQLITE_PROTOCOL) + .Const(_SC("RANGE"), SQLITE_RANGE) + .Const(_SC("READ"), SQLITE_READ) + .Const(_SC("READONLY"), SQLITE_READONLY) + .Const(_SC("READONLY_CANTLOCK"), SQLITE_READONLY_CANTLOCK) + .Const(_SC("READONLY_DBMOVED"), SQLITE_READONLY_DBMOVED) + .Const(_SC("READONLY_RECOVERY"), SQLITE_READONLY_RECOVERY) + .Const(_SC("READONLY_ROLLBACK"), SQLITE_READONLY_ROLLBACK) + .Const(_SC("RECURSIVE"), SQLITE_RECURSIVE) + .Const(_SC("REINDEX"), SQLITE_REINDEX) + .Const(_SC("REPLACE"), SQLITE_REPLACE) + .Const(_SC("ROLLBACK"), SQLITE_ROLLBACK) + .Const(_SC("ROW"), SQLITE_ROW) + .Const(_SC("SAVEPOINT"), SQLITE_SAVEPOINT) + .Const(_SC("SCANSTAT_EST"), SQLITE_SCANSTAT_EST) + .Const(_SC("SCANSTAT_EXPLAIN"), SQLITE_SCANSTAT_EXPLAIN) + .Const(_SC("SCANSTAT_NAME"), SQLITE_SCANSTAT_NAME) + .Const(_SC("SCANSTAT_NLOOP"), SQLITE_SCANSTAT_NLOOP) + .Const(_SC("SCANSTAT_NVISIT"), SQLITE_SCANSTAT_NVISIT) + .Const(_SC("SCANSTAT_SELECTID"), SQLITE_SCANSTAT_SELECTID) + .Const(_SC("SCHEMA"), SQLITE_SCHEMA) + .Const(_SC("SELECT"), SQLITE_SELECT) + .Const(_SC("SHM_EXCLUSIVE"), SQLITE_SHM_EXCLUSIVE) + .Const(_SC("SHM_LOCK"), SQLITE_SHM_LOCK) + .Const(_SC("SHM_NLOCK"), SQLITE_SHM_NLOCK) + .Const(_SC("SHM_SHARED"), SQLITE_SHM_SHARED) + .Const(_SC("SHM_UNLOCK"), SQLITE_SHM_UNLOCK) + .Const(_SC("SOURCE_ID"), SQLITE_SOURCE_ID) + .Const(_SC("STATUS_MALLOC_COUNT"), SQLITE_STATUS_MALLOC_COUNT) + .Const(_SC("STATUS_MALLOC_SIZE"), SQLITE_STATUS_MALLOC_SIZE) + .Const(_SC("STATUS_MEMORY_USED"), SQLITE_STATUS_MEMORY_USED) + .Const(_SC("STATUS_PAGECACHE_OVERFLOW"), SQLITE_STATUS_PAGECACHE_OVERFLOW) + .Const(_SC("STATUS_PAGECACHE_SIZE"), SQLITE_STATUS_PAGECACHE_SIZE) + .Const(_SC("STATUS_PAGECACHE_USED"), SQLITE_STATUS_PAGECACHE_USED) + .Const(_SC("STATUS_PARSER_STACK"), SQLITE_STATUS_PARSER_STACK) + .Const(_SC("STATUS_SCRATCH_OVERFLOW"), SQLITE_STATUS_SCRATCH_OVERFLOW) + .Const(_SC("STATUS_SCRATCH_SIZE"), SQLITE_STATUS_SCRATCH_SIZE) + .Const(_SC("STATUS_SCRATCH_USED"), SQLITE_STATUS_SCRATCH_USED) + .Const(_SC("STMTSTATUS_AUTOINDEX"), SQLITE_STMTSTATUS_AUTOINDEX) + .Const(_SC("STMTSTATUS_FULLSCAN_STEP"), SQLITE_STMTSTATUS_FULLSCAN_STEP) + .Const(_SC("STMTSTATUS_SORT"), SQLITE_STMTSTATUS_SORT) + .Const(_SC("STMTSTATUS_VM_STEP"), SQLITE_STMTSTATUS_VM_STEP) + .Const(_SC("SYNC_DATAONLY"), SQLITE_SYNC_DATAONLY) + .Const(_SC("SYNC_FULL"), SQLITE_SYNC_FULL) + .Const(_SC("SYNC_NORMAL"), SQLITE_SYNC_NORMAL) + .Const(_SC("TESTCTRL_ALWAYS"), SQLITE_TESTCTRL_ALWAYS) + .Const(_SC("TESTCTRL_ASSERT"), SQLITE_TESTCTRL_ASSERT) + .Const(_SC("TESTCTRL_BENIGN_MALLOC_HOOKS"), SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS) + .Const(_SC("TESTCTRL_BITVEC_TEST"), SQLITE_TESTCTRL_BITVEC_TEST) + .Const(_SC("TESTCTRL_BYTEORDER"), SQLITE_TESTCTRL_BYTEORDER) + .Const(_SC("TESTCTRL_EXPLAIN_STMT"), SQLITE_TESTCTRL_EXPLAIN_STMT) + .Const(_SC("TESTCTRL_FAULT_INSTALL"), SQLITE_TESTCTRL_FAULT_INSTALL) + .Const(_SC("TESTCTRL_FIRST"), SQLITE_TESTCTRL_FIRST) + .Const(_SC("TESTCTRL_IMPOSTER"), SQLITE_TESTCTRL_IMPOSTER) + .Const(_SC("TESTCTRL_ISINIT"), SQLITE_TESTCTRL_ISINIT) + .Const(_SC("TESTCTRL_ISKEYWORD"), SQLITE_TESTCTRL_ISKEYWORD) + .Const(_SC("TESTCTRL_LAST"), SQLITE_TESTCTRL_LAST) + .Const(_SC("TESTCTRL_LOCALTIME_FAULT"), SQLITE_TESTCTRL_LOCALTIME_FAULT) + .Const(_SC("TESTCTRL_NEVER_CORRUPT"), SQLITE_TESTCTRL_NEVER_CORRUPT) + .Const(_SC("TESTCTRL_OPTIMIZATIONS"), SQLITE_TESTCTRL_OPTIMIZATIONS) + .Const(_SC("TESTCTRL_PENDING_BYTE"), SQLITE_TESTCTRL_PENDING_BYTE) + .Const(_SC("TESTCTRL_PRNG_RESET"), SQLITE_TESTCTRL_PRNG_RESET) + .Const(_SC("TESTCTRL_PRNG_RESTORE"), SQLITE_TESTCTRL_PRNG_RESTORE) + .Const(_SC("TESTCTRL_PRNG_SAVE"), SQLITE_TESTCTRL_PRNG_SAVE) + .Const(_SC("TESTCTRL_RESERVE"), SQLITE_TESTCTRL_RESERVE) + .Const(_SC("TESTCTRL_SCRATCHMALLOC"), SQLITE_TESTCTRL_SCRATCHMALLOC) + .Const(_SC("TESTCTRL_SORTER_MMAP"), SQLITE_TESTCTRL_SORTER_MMAP) + .Const(_SC("TESTCTRL_VDBE_COVERAGE"), SQLITE_TESTCTRL_VDBE_COVERAGE) + .Const(_SC("TEXT"), SQLITE_TEXT) + .Const(_SC("TOOBIG"), SQLITE_TOOBIG) + .Const(_SC("TRANSACTION"), SQLITE_TRANSACTION) + .Const(_SC("UPDATE"), SQLITE_UPDATE) + .Const(_SC("UTF16"), SQLITE_UTF16) + .Const(_SC("UTF16BE"), SQLITE_UTF16BE) + .Const(_SC("UTF16LE"), SQLITE_UTF16LE) + .Const(_SC("UTF16_ALIGNED"), SQLITE_UTF16_ALIGNED) + .Const(_SC("UTF8"), SQLITE_UTF8) + .Const(_SC("VERSION"), SQLITE_VERSION) + .Const(_SC("VERSION_NUMBER"), SQLITE_VERSION_NUMBER) + .Const(_SC("VTAB_CONSTRAINT_SUPPORT"), SQLITE_VTAB_CONSTRAINT_SUPPORT) + .Const(_SC("WARNING"), SQLITE_WARNING) + .Const(_SC("WARNING_AUTOINDEX"), SQLITE_WARNING_AUTOINDEX) + ); +*/ +} + +// -------------------------------------------------------------------------------------------- +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); + printf("[SQMOD] "); + + SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); + vprintf(msg, args); + puts(""); + + SetConsoleTextAttribute(hstdout, csb_before.wAttributes); +#else + printf("%c[0;32m[SQMOD]%c[0;37m", 27, 27, msg); + vprintf(msg, args); + 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); + printf("[SQMOD] "); + + SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); + vprintf(msg, args); + puts(""); + + SetConsoleTextAttribute(hstdout, csb_before.wAttributes); +#else + printf("%c[0;32m[SQMOD]%c[0;37m", 27, 27, msg); + vprintf(msg, args); + 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) +{ + using namespace SqMod; + // Output plugin header + puts(""); + OutputMessage("--------------------------------------------------------------------"); + OutputMessage("Plugin: %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 + if (host_plugin_id < 0) + { + OutputError("%s could find the host plugin", SQSQLITE_NAME); + // Don't load! + return SQMOD_FAILURE; + } + // Should never reach this point but just in case + else if (host_plugin_id > (info->nPluginId)) + { + OutputError("%s loaded after the host plugin", SQSQLITE_NAME); + // Don't load! + return SQMOD_FAILURE; + } + // Store server proxies + _Func = functions; + _Clbk = callbacks; + _Info = info; + // Assign plugin information + _Info->uPluginVer = SQSQLITE_VERSION; + strcpy(_Info->szName, SQSQLITE_HOST_NAME); + // Bind callbacks + BindCallbacks(); + // Notify that the plugin was successfully loaded + OutputMessage("Successfully loaded %s", SQSQLITE_NAME); + // Dummy spacing + puts(""); + // Done! + return SQMOD_SUCCESS; +} diff --git a/modules/sqlite/Module.hpp b/modules/sqlite/Module.hpp new file mode 100644 index 00000000..d6c041b1 --- /dev/null +++ b/modules/sqlite/Module.hpp @@ -0,0 +1,41 @@ +#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 new file mode 100644 index 00000000..5298e067 --- /dev/null +++ b/modules/sqlite/Statement.cpp @@ -0,0 +1,1139 @@ +// ------------------------------------------------------------------------------------------------ +#include "Statement.hpp" +#include "Connection.hpp" +#include "Column.hpp" +#include "Module.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +CSStr Statement::s_BadParamI = "Unable to bind [%s] parameter at (%d) because [%s]"; +CSStr Statement::s_BadParamS = "Unable to bind [%s] parameter at (%s:%d) because [%s]"; + +// ------------------------------------------------------------------------------------------------ +SQInteger Statement::Typename(HSQUIRRELVM vm) +{ + static SQChar name[] = _SC("SqSQLiteStatement"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::Validate() const +{ + if (m_Handle) + return true; + // Invalid statement reference + _SqMod->SqThrow("Invalid SQLite statement reference"); + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::ValidateIndex(Int32 idx) const +{ + // Validate the handle + if (!m_Handle) + _SqMod->SqThrow("Invalid SQLite statement reference"); + // Is the specified index in range? + else if (!m_Handle->CheckIndex(idx)) + _SqMod->SqThrow("Index out of range: %d", idx); + // Requirements satisfied + else + return true; + // Should not proceed further + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::RowAvailable() const +{ + // Validate the handle + if (!m_Handle) + _SqMod->SqThrow("Invalid SQLite statement reference"); + // Do we have any rows available? + else if (!m_Handle->mGood) + _SqMod->SqThrow("No row available"); + // Requirements satisfied + else + return true; + // Need to reset the statement + return false; +} + +// ------------------------------------------------------------------------------------------------ +Statement::Statement() + : m_Handle() +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +Statement::Statement(const ConnHnd & connection, CSStr query) + : m_Handle(connection) +{ + if (m_Handle) + m_Handle->Create(query); + else + _SqMod->SqThrow("Unable to create the statement reference"); +} + +// ------------------------------------------------------------------------------------------------ +Statement::Statement(const Connection & connection, CSStr query) + : m_Handle(connection.GetHandle()) +{ + if (m_Handle) + m_Handle->Create(query); + else + _SqMod->SqThrow("Unable to create the statement reference"); +} + +// ------------------------------------------------------------------------------------------------ +Connection Statement::GetConnection() const +{ + // Validate the handle + if (Validate()) + return Connection(m_Handle->mConn); + // Request failed + return Connection(); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::Reset() +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Specify that we don't have a row available and we haven't finished stepping + m_Handle->mGood = false; + m_Handle->mDone = false; + // Attempt to reset the statement to it's initial state + m_Handle = sqlite3_reset(m_Handle); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow("Unable to reset statement [%s]", m_Handle.ErrStr()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::Clear() +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Specify that we don't have a row available and we haven't finished stepping + m_Handle->mGood = false; + m_Handle->mDone = false; + // Attempt to clear the statement + m_Handle = sqlite3_clear_bindings(m_Handle); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow("Unable to clear statement [%s]", m_Handle.ErrStr()); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Statement::Exec() +{ + // Validate the handle + if (!Validate()) + return -1; // Unable to proceed + // Did we reset first? + else if (m_Handle->mDone) + { + _SqMod->SqThrow("Execute without resetting first"); + // Unable to proceed + return -1; + } + // Attempt to step the statement + m_Handle = sqlite3_step(m_Handle); + // Have we finished stepping? + if (m_Handle == SQLITE_DONE) + { + // Specify that we don't have row available and we finished stepping + m_Handle->mGood = false; + m_Handle->mDone = true; + // Return the changes made by this statement + return sqlite3_changes(m_Handle->mConn); + } + // Specify that we don't have any row and we haven't finished stepping + m_Handle->mGood = false; + m_Handle->mDone = false; + // Inspect the result + switch (m_Handle->mStatus) + { + // We don't expect any rows to be returned in this case! + case SQLITE_ROW: _SqMod->SqThrow("Results were found"); + case SQLITE_BUSY: _SqMod->SqThrow("Database was busy"); + case SQLITE_ERROR: _SqMod->SqThrow("Runtime error occurred"); + case SQLITE_MISUSE: _SqMod->SqThrow("Statement misuse"); + default: _SqMod->SqThrow("Unknown failure"); + } + // Operation failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::Step() +{ + // Validate the handle + if (!Validate()) + return false; // Unable to proceed + // Did we reset first? + else if (m_Handle->mDone) + { + _SqMod->SqThrow("Stepping without resetting first"); + // Unable to proceed + return false; + } + // Attempt to step the statement + m_Handle = sqlite3_step(m_Handle); + // Do we have a row available? + if (m_Handle == SQLITE_ROW) + { + // Specify that we have a row available + return (m_Handle->mGood = true); + } + // Have we finished stepping? + else if (m_Handle == SQLITE_DONE) + { + // Specify that we finished stepping + m_Handle->mDone = true; + // Specify that we don't have a row available + return (m_Handle->mGood = false); + } + // Specify that we don't have any row and we haven't finished stepping + m_Handle->mGood = false; + m_Handle->mDone = false; + // Inspect the result + switch (m_Handle->mStatus) + { + case SQLITE_BUSY: _SqMod->SqThrow("Database was busy"); + case SQLITE_ERROR: _SqMod->SqThrow("Runtime error occurred"); + case SQLITE_MISUSE: _SqMod->SqThrow("Statement misuse"); + default: _SqMod->SqThrow("Unknown failure"); + } + // Operation failed + return false; +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindA(Int32 idx, Array & arr) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Know when to stop trying to bind values + const Int32 max = sqlite3_bind_parameter_count(m_Handle); + // Make sure that we are at least in bounds + if (idx >= max) + _SqMod->SqThrow("Parameter index out of range"); + // Should we clear onward parameters? + else if (arr.Length() <= 0) + { + // Clear all parameters after the specified index + for (Int32 idx = arr.Length(); idx < max; ++idx) + sqlite3_bind_null(m_Handle, idx); + } + // Attempt to retrieve and bind the specified values + else + { + // Allocated memory for the specified amount of values + std::vector< Object > values(arr.Length()); + // Attempt to retrieve the specified values + arr.GetArray< Object >(&values[0], values.size()); + // Obtain the start of the object list + std::vector< Object >::iterator itr = values.begin(); + // Attempt to bind each specified element individually + for (; itr != values.end() && idx < max; ++itr) + IndexBind(idx++, *itr); + } +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindI(Int32 idx, Int32 value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value + m_Handle = sqlite3_bind_int(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "int", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindL(Int32 idx, Object & value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + else + m_Handle = SQLITE_OK; + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push the specified object onto the stack + Var< Object & >::push(_SqVM, value); + // The resulted long integer value + Int64 longint = 0; + // Attempt to get the numeric value inside the specified object + if (SQ_FAILED(_SqMod->GetSLongValue(_SqVM, -1, &longint))) + _SqMod->SqThrow("Invalid long integer specified"); + // Attempt to bind the specified value + else + m_Handle = sqlite3_bind_int(m_Handle, idx, longint); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "long", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindV(Int32 idx, SQInteger value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value +#ifdef _SQ64 + m_Handle = sqlite3_bind_int64(m_Handle, idx, value); +#else + m_Handle = sqlite3_bind_int(m_Handle, idx, value); +#endif // _SQ64 + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "value", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindF(Int32 idx, SQFloat value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value + m_Handle = sqlite3_bind_double(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "float", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindS(Int32 idx, CSStr value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value + m_Handle = sqlite3_bind_text(m_Handle, idx, value, -1, SQLITE_TRANSIENT); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "string", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindB(Int32 idx, bool value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value + m_Handle = sqlite3_bind_int(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "boolean", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBindN(Int32 idx) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to bind the specified value + m_Handle = sqlite3_bind_null(m_Handle, idx); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "null", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindT(Table & tbl) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Should we clear the all the parameters? + else if (tbl.GetSize() <= 0) + Clear(); + // Attempt to assign the specified parameter values + else + { + Table::iterator itr; + Object val; + // Process each element until _next returns null + while (tbl.Next(itr)) + { + // Get the value object + val = Object(itr.getValue()); + // Bind it to the associated key + NameBind(itr.getName(), val); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindI(CSStr name, Int32 value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value + m_Handle = sqlite3_bind_int(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "int", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindL(CSStr name, Object & value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + else + m_Handle = SQLITE_OK; + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Push the specified object onto the stack + Var< Object & >::push(_SqVM, value); + // The resulted long integer value + Uint64 longint = 0; + // Attempt to get the numeric value inside the specified object + if (SQ_FAILED(_SqMod->GetULongValue(_SqVM, -1, &longint))) + _SqMod->SqThrow("Unknown long integer specified"); + // Attempt to bind the specified value + else + m_Handle = sqlite3_bind_int(m_Handle, idx, longint); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "long", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindV(CSStr name, SQInteger value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value +#ifdef _SQ64 + m_Handle = sqlite3_bind_int64(m_Handle, idx, value); +#else + m_Handle = sqlite3_bind_int(m_Handle, idx, value); +#endif // _SQ64 + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "value", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindF(CSStr name, SQFloat value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value + m_Handle = sqlite3_bind_double(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "float", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindS(CSStr name, CSStr value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value + m_Handle = sqlite3_bind_text(m_Handle, idx, value, -1, SQLITE_TRANSIENT); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "string", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindB(CSStr name, bool value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value + m_Handle = sqlite3_bind_int(m_Handle, idx, value); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "boolean", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBindN(CSStr name) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to bind the specified value + m_Handle = sqlite3_bind_null(m_Handle, idx); + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "null", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::IndexBind(Int32 idx, Object & value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + else + m_Handle = SQLITE_OK; + // Attempt to identify the specified type + switch (value.GetType()) + { + // Is this an integer? + case OT_INTEGER: +#ifdef _SQ64 + m_Handle = sqlite3_bind_int64(m_Handle, idx, value.Cast< SQInteger >()); +#else + m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< SQInteger >()); +#endif + break; + // Is this a float? + case OT_FLOAT: + m_Handle = sqlite3_bind_double(m_Handle, idx, value.Cast< SQFloat >()); + break; + // Is this a boolean? + case OT_BOOL: + m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< bool >()); + break; + // Is this a string? + case OT_STRING: + { + CSStr str = value.Cast< CSStr >(); + m_Handle = sqlite3_bind_text(m_Handle, idx, str, -1, SQLITE_TRANSIENT); + } break; + // Is this a null value? + case OT_NULL: + m_Handle = sqlite3_bind_null(m_Handle, idx); + break; + // Unsupported type + default: + { + _SqMod->SqThrow("Attempting to bind unknown value type (%d)", idx); + // Unable to proceed + return; + } + } + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamI, "auto", idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +void Statement::NameBind(CSStr name, Object & value) +{ + // Validate the handle + if (!Validate()) + return; // Unable to proceed + // Attempt to obtain the index of the specified parameter name + const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name); + // Validate the obtained index + if (!idx) + { + _SqMod->SqThrow("Unknown parameter named (%s)", name); + // Unable to proceed + return; + } + // Attempt to identify the specified type + switch (value.GetType()) + { + // Is this an integer? + case OT_INTEGER: +#ifdef _SQ64 + m_Handle = sqlite3_bind_int64(m_Handle, idx, value.Cast< SQInteger >()); +#else + m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< SQInteger >()); +#endif + break; + // Is this a floating point? + case OT_FLOAT: + m_Handle = sqlite3_bind_double(m_Handle, idx, value.Cast< SQFloat >()); + break; + // Is this a boolean? + case OT_BOOL: + m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< bool >()); + break; + // Is this a string? + case OT_STRING: + { + CSStr str = value.Cast< CSStr >(); + m_Handle = sqlite3_bind_text(m_Handle, idx, str, -1, SQLITE_TRANSIENT); + } break; + // Is this a null value? + case OT_NULL: + m_Handle = sqlite3_bind_null(m_Handle, idx); + break; + // Unsupported type + default: + { + _SqMod->SqThrow("Attempting to bind unknown value type (%s:%d)", name, idx); + // Unable to proceed + return; + } + } + // Validate the result + if (m_Handle != SQLITE_OK) + _SqMod->SqThrow(s_BadParamS, "auto", name, idx, m_Handle.ErrMsg()); +} + +// ------------------------------------------------------------------------------------------------ +Object Statement::FetchColumnIndex(Int32 idx) const +{ + // Validate the handle and row + if (!RowAvailable()) + return Object(); // Unable to proceed + // Is the specified index valid? + else if (!m_Handle->CheckIndex(idx)) + { + _SqMod->SqThrow("Index out of range: %d", idx); + // Unable to proceed + return Object(); + } + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Identify which type of value must be pushed on the stack + switch (sqlite3_column_type(m_Handle, idx)) + { + // Is this a null value? + case SQLITE_NULL: + sq_pushnull(_SqVM); + break; + // Is this an integer? + case SQLITE_INTEGER: +#ifdef _SQ64 + sq_pushinteger(_SqVM, sqlite3_column_int64(m_Handle, idx)); +#else + sq_pushinteger(_SqVM, sqlite3_column_int(m_Handle, idx)); +#endif + // Is this a floating point? + case SQLITE_FLOAT: + sq_pushfloat(_SqVM, sqlite3_column_double(m_Handle, idx)); + // Is this a string? + case SQLITE_TEXT: + sq_pushstring(_SqVM, (CSStr)sqlite3_column_text(m_Handle, idx), + sqlite3_column_bytes(m_Handle, idx)); + // Is this raw data? + case SQLITE_BLOB: + { + // Obtain the size of the data + const Int32 sz = sqlite3_column_bytes(m_Handle, idx); + // Allocate a blob of the same size + SQUserPointer p = sqstd_createblob(_SqVM, sz); + // Obtain a pointer to the data + const void * b = sqlite3_column_blob(m_Handle, idx); + // Could the memory blob be allocated? + if (!p) + { + _SqMod->SqThrow("Unable to allocate space for column blob value"); + // Default to null + sq_pushnull(_SqVM); + } + // Is there any data to read? + else if (!b) + { + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Now throw the error + _SqMod->SqThrow("Unable to read data from column blob value"); + // Default to null + sq_pushnull(_SqVM); + } + // Copy the data into the memory blob + else + memcpy(p, b, sz); + } + // Unknown type + default: + { + _SqMod->SqThrow("Unknown value to fetch at index: %d", idx); + // Default to null + sq_pushnull(_SqVM); + } + } + // Obtain the object with the value from the stack + Var< Object > obj(_SqVM, -1); + // Remove an pushed values (if any) to restore the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Now return the obtained object + return obj.value; +} + +// ------------------------------------------------------------------------------------------------ +Object Statement::FetchColumnName(CSStr name) const +{ + // Validate the handle and row + if (RowAvailable()) + return FetchColumnIndex(m_Handle->GetColumnIndex(name)); + // Request failed + return Object(); +} + +// ------------------------------------------------------------------------------------------------ +Array Statement::FetchArray() const +{ + // Validate the handle + if (Validate()) + return FetchArray(0, m_Handle->mColumns-1); + // Request failed + return Array(); +} + +// ------------------------------------------------------------------------------------------------ +Array Statement::FetchArray(Int32 min) const +{ + // Validate the handle + if (Validate()) + return FetchArray(min, m_Handle->mColumns-1); + // Request failed + return Array(); +} + +// ------------------------------------------------------------------------------------------------ +Array Statement::FetchArray(Int32 min, Int32 max) const +{ + // Validate the handle + if (!RowAvailable()) + return Array(); // Unable to proceed + // Was there anything selected? + else if (min == max) + return Array(); // Nothing to retrieve + // Is the minimum actually the minimum? + else if (min > max) + { + _SqMod->SqThrow("Minimum is higher than maximum"); + // Unable to proceed + return Array(); + } + // Is the minimum in range> + else if (!m_Handle->CheckIndex(min)) + { + _SqMod->SqThrow("Minimum is out of range"); + // Unable to proceed + return Array(); + } + // Is the maximum in range? + else if (!m_Handle->CheckIndex(max)) + { + _SqMod->SqThrow("Maximum is out of range"); + // Unable to proceed + return Array(); + } + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Allocate an array large enough to hold the values from selected columns + Array arr(_SqVM, max-min); + // Process the range of selected columns + for (Int32 elem = 0, idx = min; idx < max; ++elem, ++idx) + { + // Identify the type of value that must be assigned + switch (sqlite3_column_type(m_Handle, idx)) + { + // Is this a null value? + case SQLITE_NULL: + // Array elements are initialized as null by default so we just leave it like that + break; + // Is this an integer? + case SQLITE_INTEGER: +#ifdef _SQ64 + arr.SetValue(elem, sqlite3_column_int64(m_Handle, idx)); +#else + arr.SetValue(elem, sqlite3_column_int(m_Handle, idx)); +#endif + break; + // Is this a floating point? + case SQLITE_FLOAT: + arr.SetValue(elem, sqlite3_column_double(m_Handle, idx)); + break; + // Is this a string? + case SQLITE_TEXT: + arr.SetValue(elem, (CCStr)(sqlite3_column_text(m_Handle, idx))); + break; + // Is this raw data? + case SQLITE_BLOB: + { + // Obtain the size of the data + const Int32 sz = sqlite3_column_bytes(m_Handle, idx); + // Allocate a blob of the same size + SQUserPointer p = sqstd_createblob(_SqVM, sz); + // Obtain a pointer to the data + const void * b = sqlite3_column_blob(m_Handle, idx); + // Could the memory blob be allocated? + if (!p) + { + _SqMod->SqThrow("Unable to allocate space for column blob value"); + // Unable to proceed + return Array(); + } + // Is there any data to read? + else if (!b) + { + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Now throw the error + _SqMod->SqThrow("Unable to read data from column blob value"); + // Unable to proceed + return Array(); + } + else + { + // Copy the data into the memory blob + memcpy(p, b, sz); + // Obtain the object from the stack + Var< Object > obj(_SqVM, -1); + // Bind it as an array element + arr.Bind(elem, obj.value); + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + } + } + break; + // Unknown type + default: + _SqMod->SqThrow("Unknown value to fetch at index: %d", idx); + // Unable to proceed + return Array(); + } + } + // Return the resulted array + return arr; +} + +// ------------------------------------------------------------------------------------------------ +Table Statement::FetchTable() const +{ + // Validate the handle + if (Validate()) + return FetchTable(0, m_Handle->mColumns-1); + // Request failed + return Table(); +} + +// ------------------------------------------------------------------------------------------------ +Table Statement::FetchTable(Int32 min) const +{ + // Validate the handle + if (Validate()) + return FetchTable(min, m_Handle->mColumns-1); + // Request failed + return Table(); +} + +// ------------------------------------------------------------------------------------------------ +Table Statement::FetchTable(Int32 min, Int32 max) const +{ + // Validate the handle + if (!RowAvailable()) + return Table(); // Unable to proceed + // Was there anything selected? + else if (min == max) + return Table(); // Nothing to retrieve + // Is the minimum actually the minimum? + else if (min > max) + { + _SqMod->SqThrow("Minimum is higher than maximum"); + // Unable to proceed + return Table(); + } + // Is the minimum in range> + else if (!m_Handle->CheckIndex(min)) + { + _SqMod->SqThrow("Minimum is out of range"); + // Unable to proceed + return Table(); + } + // Is the maximum in range? + else if (!m_Handle->CheckIndex(max)) + { + _SqMod->SqThrow("Maximum is out of range"); + // Unable to proceed + return Table(); + } + // Obtain the initial stack size + const Int32 top = sq_gettop(_SqVM); + // Allocate an array large enough to hold the values from selected columns + Table tbl(_SqVM); + // Used to bind null values + Object obj; + // Process the range of selected columns + for (Int32 elem = 0, idx = min; idx < max; ++elem, ++idx) + { + //Attempt to obtain the column name + CSStr name = sqlite3_column_name(m_Handle, idx); + // Validate the obtained name + if (!name) + { + _SqMod->SqThrow("Unable to retrieve name of column (%d)", idx); + // Unable to proceed + return Table(); + } + // Identify the type of value that must be assigned + switch (sqlite3_column_type(m_Handle, idx)) + { + // Is this a null value? + case SQLITE_NULL: + { + // Make sure we don't insert the same instance + obj.Release(); + // Now it's safe to insert the value + tbl.SetValue(name, obj); + } + break; + // Is this an integer? + case SQLITE_INTEGER: +#ifdef _SQ64 + tbl.SetValue(name, sqlite3_column_int64(m_Handle, idx)); +#else + tbl.SetValue(name, sqlite3_column_int(m_Handle, idx)); +#endif + break; + // Is this a floating point? + case SQLITE_FLOAT: + tbl.SetValue(name, sqlite3_column_double(m_Handle, idx)); + break; + // Is this a string? + case SQLITE_TEXT: + tbl.SetValue(name, (CCStr)(sqlite3_column_text(m_Handle, idx))); + break; + // Is this raw data? + case SQLITE_BLOB: + { + // Obtain the size of the data + const Int32 sz = sqlite3_column_bytes(m_Handle, idx); + // Allocate a blob of the same size + SQUserPointer p = sqstd_createblob(_SqVM, sz); + // Obtain a pointer to the data + const void * b = sqlite3_column_blob(m_Handle, idx); + // Could the memory blob be allocated? + if (!p) + { + _SqMod->SqThrow("Unable to allocate space for column blob value"); + // Unable to proceed + return Table(); + } + // Is there any data to read? + else if (!b) + { + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + // Now throw the error + _SqMod->SqThrow("Unable to read data from column blob value"); + // Unable to proceed + return Table(); + } + else + { + // Copy the data into the memory blob + memcpy(p, b, sz); + // Obtain the object from the stack + Var< Object > obj(_SqVM, -1); + // Bind it as a table element + tbl.Bind(name, obj.value); + // Pop the memory blob from the stack + sq_pop(_SqVM, sq_gettop(_SqVM) - top); + } + } + break; + // Unknown type + default: + _SqMod->SqThrow("Unknown value to fetch at index: %d", idx); + // Unable to proceed + return Table(); + } + } + // Return the resulted table + return tbl; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::CheckIndex(Int32 idx) const +{ + // Validate the handle + if (Validate()) + return m_Handle->CheckIndex(idx); + // Invalid statement is same as invalid index + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool Statement::IsColumnNull(Int32 idx) const +{ + // Can we make the request? + if (ValidateIndex(idx)) + return (sqlite3_column_type(m_Handle, idx) == SQLITE_NULL); + // Request failed + return true; +} + +// ------------------------------------------------------------------------------------------------ +Int32 Statement::GetColumnIndex(CSStr name) const +{ + // Validate the handle + if (Validate()) + return m_Handle->GetColumnIndex(name); + // Invalid statement is same as invalid index + return -1; +} + +// ------------------------------------------------------------------------------------------------ +CSStr Statement::GetColumnName(Int32 idx) const +{ + // Can we make the request? + if (ValidateIndex(idx)) + return sqlite3_column_name(m_Handle, idx); + // Request failed + return _SC(""); +} + +// ------------------------------------------------------------------------------------------------ +CSStr Statement::GetColumnOriginName(Int32 idx) const +{ +#ifdef SQLITE_ENABLE_COLUMN_METADATA + // Can we make the request? + if (ValidateIndex(idx)) + return sqlite3_column_origin_name(m_Handle, idx); +#else + SQMOD_UNUSED_VAR(idx); + _SqMod->SqThrow("The module was compiled without this feature"); +#endif + // Request failed + return _SC(""); +} + +// ------------------------------------------------------------------------------------------------ +Int32 Statement::GetColumnType(Int32 idx) const +{ + // Can we make the request? + if (ValidateIndex(idx)) + return sqlite3_column_type(m_Handle, idx); + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Int32 Statement::GetColumnBytes(Int32 idx) const +{ + // Can we make the request? + if (ValidateIndex(idx)) + return sqlite3_column_bytes(m_Handle, idx); + // Request failed + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Column Statement::GetColumnByIndex(Int32 idx) const +{ + // Can we make the request? + if (ValidateIndex(idx)) + return Column(m_Handle, idx); + // Request failed + return Column(); +} + +// ------------------------------------------------------------------------------------------------ +Column Statement::GetColumnByName(CSStr name) const +{ + // Validate the handle + if (Validate()) + { + // Attempt to obtain the requested column index + const Int32 idx = m_Handle->GetColumnIndex(name); + // Validate the obtained index + if (idx < 0) + _SqMod->SqThrow("Unknown column named (%s)", name); + // Return the requested column + else + return Column(m_Handle, idx); + } + // Request failed + return Column(); +} + +} // Namespace:: SqMod diff --git a/modules/sqlite/Statement.hpp b/modules/sqlite/Statement.hpp new file mode 100644 index 00000000..2bf48b97 --- /dev/null +++ b/modules/sqlite/Statement.hpp @@ -0,0 +1,499 @@ +#ifndef _SQSQLITE_STATEMENT_HPP +#define _SQSQLITE_STATEMENT_HPP + +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" +#include "SqAPI.h" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Class used to manage an SQLite statement. +*/ +class Statement +{ +private: + + // -------------------------------------------------------------------------------------------- + StmtHnd m_Handle; /* The handle to the managed database statement resource. */ + + // -------------------------------------------------------------------------------------------- + static CSStr s_BadParamI; /* Error message when failed to bind value to parameter index. */ + static CSStr s_BadParamS; /* Error message when failed to bind value to parameter name. */ + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the statement reference and throw an error if invalid. + */ + bool Validate() const; + + /* -------------------------------------------------------------------------------------------- + * Validate the statement reference and index, and throw an error if they're invalid. + */ + bool ValidateIndex(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Validate the statement reference and row, and throw an error if they're invalid. + */ + bool RowAvailable() const; + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + Statement(); + + /* -------------------------------------------------------------------------------------------- + * Construct a statement under the specified connection using the specified string. + */ + Statement(const ConnHnd & connection, CSStr query); + + /* -------------------------------------------------------------------------------------------- + * Construct a statement under the specified connection using the specified string. + */ + Statement(const Connection & connection, CSStr query); + + /* -------------------------------------------------------------------------------------------- + * Direct handle constructor. + */ + Statement(const StmtHnd & h) + : m_Handle(h) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Statement(const Statement & o) + : m_Handle(o.m_Handle) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~Statement() + { + /* Let the reference manager destroy the statement when necessary. */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Statement & operator = (const Statement & o) + { + m_Handle = o.m_Handle; + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Perform an equality comparison between two connections. + */ + bool operator == (const Statement & o) const + { + return (m_Handle.m_Hnd == o.m_Handle.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Perform an inequality comparison between two connections. + */ + bool operator != (const Statement & o) const + { + return (m_Handle.m_Hnd != o.m_Handle.m_Hnd); + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the raw connection handle. + */ + operator sqlite3_stmt * () + { + return (sqlite3_stmt *)m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to the raw connection handle. + */ + operator sqlite3_stmt * () const + { + return (sqlite3_stmt *)m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to compare two instances of this type. + */ + Int32 Cmp(const Statement & o) const + { + if (m_Handle.m_Hnd == o.m_Handle.m_Hnd) + return 0; + else if (m_Handle.m_Hnd > o.m_Handle.m_Hnd) + return 1; + else + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + CSStr ToString() const + { + // Validate the handle + if (m_Handle) + return m_Handle->mQuery.c_str(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this statement is valid. + */ + bool IsValid() const + { + return (bool)m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated statement handle. + */ + const StmtHnd & GetHandle() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to this statement handle. + */ + Uint32 GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated database connection. + */ + Connection GetConnection() const; + + /* -------------------------------------------------------------------------------------------- + * Release the reference to the associated database statement. + */ + void Release() + { + m_Handle.Drop(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the last received status code. + */ + Int32 GetStatus() const + { + // Validate the handle + if (Validate()) + return m_Handle->mStatus; + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Return the numeric result code for the most recent failed API call (if any). + */ + Int32 GetErrorCode() const + { + // Validate the handle + if (Validate()) + return m_Handle.ErrNo(); + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Return the extended numeric result code for the most recent failed API call (if any). + */ + Int32 GetExtendedErrorCode() const + { + // Validate the handle + if (Validate()) + return m_Handle.ExErrNo(); + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the message of the last received error code. + */ + CSStr GetErrStr() const + { + // Validate the handle + if (Validate()) + return m_Handle.ErrStr(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Return the last error message associated with this database connection. + */ + CSStr GetErrMsg() const + { + // Validate the handle + if (Validate()) + return m_Handle.ErrMsg(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the amount of requested columns. + */ + Int32 GetColumns() const + { + // Validate the handle + if (Validate()) + return m_Handle->mColumns; + // Request failed + return -1; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the query string used to create this statement. + */ + CSStr GetQuery() const + { + // Validate the handle + if (Validate()) + return m_Handle->mQuery.c_str(); + // Request failed + return _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * See if the last step retrieved a valid row. + */ + bool GetGood() const + { + // Validate the handle + if (Validate()) + return m_Handle->mGood; + // Request failed + return false; + } + + /* -------------------------------------------------------------------------------------------- + * See if there are any steps left. + */ + bool GetDone() const + { + // Validate the handle + if (Validate()) + return m_Handle->mDone; + // Request failed + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Reset the statement back to its initial position to be stepped again. + */ + void Reset(); + + /* -------------------------------------------------------------------------------------------- + * Clear any values binded to this statement. + */ + void Clear(); + + /* -------------------------------------------------------------------------------------------- + * Execute this statement and don't expect any rows to be returned. + */ + Int32 Exec(); + + /* -------------------------------------------------------------------------------------------- + * Step the statement and expect a row to be returned. + */ + bool Step(); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the values from an array starting at the specified index. + */ + void IndexBindA(Int32 idx, Array & arr); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a integer value at the the specified parameter index. + */ + void IndexBindI(Int32 idx, Int32 value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a long integer value at the the specified parameter index. + */ + void IndexBindL(Int32 idx, Object & value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a native integer value at the the specified parameter index. + */ + void IndexBindV(Int32 idx, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a floating point value at the the specified parameter index. + */ + void IndexBindF(Int32 idx, SQFloat value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a string value at the the specified parameter index. + */ + void IndexBindS(Int32 idx, CSStr value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a boolean value at the the specified parameter index. + */ + void IndexBindB(Int32 idx, bool value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a null value at the the specified parameter index. + */ + void IndexBindN(Int32 idx); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the values from an associative container. + */ + void NameBindT(Table & tbl); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a integer value at the specified parameter name. + */ + void NameBindI(CSStr name, Int32 value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a long integer value at the specified parameter name. + */ + void NameBindL(CSStr name, Object & value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a native integer value at the specified parameter name. + */ + void NameBindV(CSStr name, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a floating point value at the specified parameter name. + */ + void NameBindF(CSStr name, SQFloat value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a string value at the specified parameter name. + */ + void NameBindS(CSStr name, CSStr value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a boolean value at the specified parameter name. + */ + void NameBindB(CSStr name, bool value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the a null value at the specified parameter name. + */ + void NameBindN(CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the specified value at the specified parameter index. + */ + void IndexBind(Int32 idx, Object & value); + + /* -------------------------------------------------------------------------------------------- + * Attempt to bind the specified value at the specified parameter name. + */ + void NameBind(CSStr name, Object & value); + + /* -------------------------------------------------------------------------------------------- + * Fetch the value at the specifie column index. + */ + Object FetchColumnIndex(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the value at the specifie column name. + */ + Object FetchColumnName(CSStr name) const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an array container. + */ + Array FetchArray() const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an array container. + */ + Array FetchArray(Int32 min) const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an array container. + */ + Array FetchArray(Int32 min, Int32 max) const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an associative container. + */ + Table FetchTable() const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an associative container. + */ + Table FetchTable(Int32 min) const; + + /* -------------------------------------------------------------------------------------------- + * Fetch the row as an associative container. + */ + Table FetchTable(Int32 min, Int32 max) const; + + /* -------------------------------------------------------------------------------------------- + * Check whether a specific index is in range. + */ + bool CheckIndex(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Check whether the specified column is null. + */ + bool IsColumnNull(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column index associated with the specified name. + */ + Int32 GetColumnIndex(CSStr name) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column name associated with the specified index. + */ + CSStr GetColumnName(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column origin name if the library was compiled with such feature. + */ + CSStr GetColumnOriginName(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the type identifier of the column associated with the specified index. + */ + Int32 GetColumnType(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the size in bytes of the column associated with the specified index. + */ + Int32 GetColumnBytes(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column with the specified index. + */ + Column GetColumnByIndex(Int32 idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the column with the specified name. + */ + Column GetColumnByName(CSStr name) const; +}; + +} // Namespace:: SqMod + +#endif // _SQSQLITE_STATEMENT_HPP