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