1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-01-31 09:57:14 +01:00

Switched the SQLite module to use C++ exceptions to fix the Sqrat issues and gain significantly more performance.

Also fixed various other issues in the SQLite module.
This commit is contained in:
Sandu Liviu Catalin 2016-02-27 17:53:12 +02:00
parent 8340a5dbc4
commit 331b03028c
10 changed files with 582 additions and 832 deletions

View File

@ -363,6 +363,7 @@
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
<Add option="-DSQMOD_PLUGIN_API" /> <Add option="-DSQMOD_PLUGIN_API" />
<Add option="-DSCRAT_USE_EXCEPTIONS" />
<Add directory="../modules/sqlite" /> <Add directory="../modules/sqlite" />
<Add directory="../shared" /> <Add directory="../shared" />
<Add directory="../include" /> <Add directory="../include" />

View File

@ -19,107 +19,91 @@ SQInteger Column::Typename(HSQUIRRELVM vm)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Column::Validate() const void Column::Validate() const
{ {
// Are we pointing to a valid index? // Are we pointing to a valid index?
if (m_Index < 0) if (m_Index < 0)
_SqMod->SqThrow("Invalid column index"); SqThrowF("Invalid column index");
// Do we belong to a valid statement? // Do we belong to a valid statement?
else if (!m_Stmt) else if (!m_Stmt)
_SqMod->SqThrow("Invalid SQLite statement reference"); SqThrowF("Invalid SQLite statement reference");
// Requirements satisfied
else
return true;
// Validation failed
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Column::RowAvailable() const void Column::ValidateRow() const
{ {
// Are we pointing to a valid index? // Are we pointing to a valid index?
if (m_Index < 0) if (m_Index < 0)
_SqMod->SqThrow("Invalid column index"); SqThrowF("Invalid column index");
// Do we belong to a valid statement? // Do we belong to a valid statement?
else if (!m_Stmt) else if (!m_Stmt)
_SqMod->SqThrow("Invalid SQLite statement reference"); SqThrowF("Invalid SQLite statement reference");
// Do we have any rows available? // Do we have any rows available?
else if (!m_Stmt->mGood) else if (!m_Stmt->mGood)
_SqMod->SqThrow("No row available"); SqThrowF("No row available");
// Requirements satisfied
else
return true;
// Validation failed
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Column::GetStatement() const Object Column::GetStatement() const
{ {
// Validate the handle // Validate the column
if (Validate()) Validate();
// Return the requested information
return Object(new Statement(m_Stmt)); return Object(new Statement(m_Stmt));
// Request failed
return Object(new Statement());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Column::GetConnection() const Object Column::GetConnection() const
{ {
// Validate the handle // Validate the column
if (Validate()) Validate();
// Return the requested information
return Object(new Connection(m_Stmt->mConn)); return Object(new Connection(m_Stmt->mConn));
// Request failed
return Object(new Connection());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Column::GetNumber() const Int32 Column::GetNumber() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
// Return the requested information
return sqlite3_column_int(m_Stmt, m_Index); return sqlite3_column_int(m_Stmt, m_Index);
// Request failed
return 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
SQInteger Column::GetInteger() const SQInteger Column::GetInteger() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
// Return the requested information
#ifdef _SQ64 #ifdef _SQ64
return sqlite3_column_int64(m_Stmt, m_Index); return sqlite3_column_int64(m_Stmt, m_Index);
#else #else
return sqlite3_column_int(m_Stmt, m_Index); return sqlite3_column_int(m_Stmt, m_Index);
#endif #endif
// Request failed
return 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
SQFloat Column::GetFloat() const SQFloat Column::GetFloat() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
// Return the requested information
return (SQFloat)sqlite3_column_double(m_Stmt, m_Index); return (SQFloat)sqlite3_column_double(m_Stmt, m_Index);
// Request failed
return SQFloat(0.0);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Column::GetLong() const Object Column::GetLong() const
{ {
// Validate the handle and index // Validate the column and statement row
if (!RowAvailable()) ValidateRow();
return Object(); // Request failed
// Obtain the initial stack size // Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM); const Int32 top = sq_gettop(_SqVM);
// Push a long integer instance with the requested value on the stack // Push a long integer instance with the requested value on the stack
_SqMod->PushSLongObject(_SqVM, sqlite3_column_int64(m_Stmt, m_Index)); _SqMod->PushSLongObject(_SqVM, sqlite3_column_int64(m_Stmt, m_Index));
// Obtain the object from the stack // Obtain the object from the stack
Var< Object > inst(_SqVM, -1); Var< Object > inst(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack // Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance // Return the long integer instance
return inst.value; return inst.value;
@ -128,9 +112,8 @@ Object Column::GetLong() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Column::GetString() const Object Column::GetString() const
{ {
// Validate the handle and index // Validate the column and statement row
if (!RowAvailable()) ValidateRow();
return Object(); // Request failed
// Obtain the initial stack size // Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM); const Int32 top = sq_gettop(_SqVM);
// Push the column text on the stack // Push the column text on the stack
@ -138,7 +121,7 @@ Object Column::GetString() const
sqlite3_column_bytes(m_Stmt, m_Index)); sqlite3_column_bytes(m_Stmt, m_Index));
// Obtain the object from the stack // Obtain the object from the stack
Var< Object > inst(_SqVM, -1); Var< Object > inst(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack // Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance // Return the long integer instance
return inst.value; return inst.value;
@ -147,19 +130,17 @@ Object Column::GetString() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Column::GetBoolean() const bool Column::GetBoolean() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
// Return the requested information
return sqlite3_column_int(m_Stmt, m_Index) > 0; return sqlite3_column_int(m_Stmt, m_Index) > 0;
// Request failed
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Column::GetBlob() const Object Column::GetBlob() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
return Object(); // Request failed
// Obtain the initial stack size // Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM); const Int32 top = sq_gettop(_SqVM);
// Obtain the size of the data // Obtain the size of the data
@ -170,71 +151,63 @@ Object Column::GetBlob() const
const void * b = sqlite3_column_blob(m_Stmt, m_Index); const void * b = sqlite3_column_blob(m_Stmt, m_Index);
// Could the memory blob be allocated? // Could the memory blob be allocated?
if (!p) if (!p)
{ SqThrowF("Unable to allocate space for column blob value");
_SqMod->SqThrow("Unable to allocate space for column blob value");
// Request failed
return Object();
}
// Is there any data to read? // Is there any data to read?
else if (!b) else if (!b)
{ {
// Pop the memory blob from the stack // Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now throw the error // Push a null value instead
_SqMod->SqThrow("Unable to read data from column blob value"); sq_pushnull(_SqVM);
// Request failed
return Object();
} }
// Copy the data into the memory blob // Copy the data into the memory blob
else else
memcpy(p, b, sz); memcpy(p, b, sz);
// Obtain the object from the stack // Obtain the object from the stack
Var< Object > inst(_SqVM, -1); Var< Object > inst(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack // Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance // Return the blob instance
return inst.value; return inst.value;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
SQChar Column::GetChar() const SQChar Column::GetChar() const
{ {
// Validate the handle and index // Validate the column and statement row
if (RowAvailable()) ValidateRow();
// Return the requested information
return (SQChar)sqlite3_column_int(m_Stmt, m_Index); return (SQChar)sqlite3_column_int(m_Stmt, m_Index);
// Request failed
return 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Column::IsNull() const bool Column::IsNull() const
{ {
// Can we make the request? // Validate the column
if (Validate()) Validate();
// Return the requested information
return (sqlite3_column_type(m_Stmt, m_Index) == SQLITE_NULL); return (sqlite3_column_type(m_Stmt, m_Index) == SQLITE_NULL);
// Request failed
return true;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CSStr Column::GetName() const CSStr Column::GetName() const
{ {
// Can we make the request? // Validate the column
if (Validate()) Validate();
// Return the requested information
return sqlite3_column_name(m_Stmt, m_Index); return sqlite3_column_name(m_Stmt, m_Index);
// Request failed
return _SC("");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CSStr Column::GetOriginName() const CSStr Column::GetOriginName() const
{ {
#ifdef SQLITE_ENABLE_COLUMN_METADATA #ifdef SQLITE_ENABLE_COLUMN_METADATA
// Can we make the request? // Validate the column
if (Validate()) Validate();
// Return the requested information
return sqlite3_column_origin_name(m_Stmt, m_Index); return sqlite3_column_origin_name(m_Stmt, m_Index);
#else #else
_SqMod->SqThrow("The module was compiled without this feature"); SqThrowF("The module was compiled without this feature");
#endif #endif
// Request failed // Request failed
return _SC(""); return _SC("");
@ -243,22 +216,19 @@ CSStr Column::GetOriginName() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Column::GetType() const Int32 Column::GetType() const
{ {
// Can we make the request? // Validate the column
if (Validate()) Validate();
// Return the requested information
return sqlite3_column_type(m_Stmt, m_Index); return sqlite3_column_type(m_Stmt, m_Index);
// Request failed
return -1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Column::GetBytes() const Int32 Column::GetBytes() const
{ {
// Can we make the request? // Validate the column
if (Validate()) Validate();
// Return the requested information
return sqlite3_column_bytes(m_Stmt, m_Index); return sqlite3_column_bytes(m_Stmt, m_Index);
// Request failed
return -1;
} }
} // Namespace:: SqMod } // Namespace:: SqMod

View File

@ -27,12 +27,12 @@ private:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the statement reference and index, and throw an error if they're invalid. * Validate the statement reference and index, and throw an error if they're invalid.
*/ */
bool Validate() const; void Validate() const;
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the statement reference, index and row, and throw an error if they're invalid. * Validate the statement reference, index and row, and throw an error if they're invalid.
*/ */
bool RowAvailable() const; void ValidateRow() const;
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Base constructor. * Base constructor.

View File

@ -15,6 +15,27 @@ static SQChar g_Buffer[4096]; // Common buffer to reduce memory allocations.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
namespace SqMod { namespace SqMod {
// ------------------------------------------------------------------------------------------------
SStr GetTempBuff()
{
return g_Buffer;
}
// ------------------------------------------------------------------------------------------------
void SqThrowF(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)
strcpy(g_Buffer, "Unknown error has occurred");
// Release the argument list
va_end(args);
// Throw the exception with the resulted message
throw Sqrat::Exception(g_Buffer);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CSStr FmtStr(CSStr str, ...) CSStr FmtStr(CSStr str, ...)
{ {
@ -72,7 +93,7 @@ ConnHnd::Handle::~Handle()
// Are we dealing with a memory leak? Technically shouldn't reach this situation! // Are we dealing with a memory leak? Technically shouldn't reach this situation!
else if (mRef != 0) else if (mRef != 0)
// Should we deal with undefined behavior instead? How bad is one connection left open? // Should we deal with undefined behavior instead? How bad is one connection left open?
_SqMod->LogErr("SQLite connection is still referenced."); _SqMod->LogErr("SQLite connection is still referenced (%s)", mName.c_str());
else else
{ {
// NOTE: Should we call sqlite3_interrupt(...) before closing? // NOTE: Should we call sqlite3_interrupt(...) before closing?
@ -91,18 +112,10 @@ void ConnHnd::Handle::Create(CSStr name, Int32 flags, CSStr vfs)
{ {
// Make sure a previous connection doesn't exist // Make sure a previous connection doesn't exist
if (mPtr) if (mPtr)
{ SqThrowF("Unable to connect to database. Database already connected");
_SqMod->SqThrow("Unable to connect to database. Database already connected");
// Unable to proceed
return;
}
// Make sure the name is valid // Make sure the name is valid
else if (!name || strlen(name) <= 0) else if (!name || strlen(name) <= 0)
{ SqThrowF("Unable to connect to database. The name is invalid");
_SqMod->SqThrow("Unable to connect to database. The name is invalid");
// Unable to proceed
return;
}
// Attempt to create the database connection // Attempt to create the database connection
else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK) else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK)
{ {
@ -111,9 +124,7 @@ void ConnHnd::Handle::Create(CSStr name, Int32 flags, CSStr vfs)
// Explicitly make sure it's null // Explicitly make sure it's null
mPtr = NULL; mPtr = NULL;
// Now its safe to throw the error // Now its safe to throw the error
_SqMod->SqThrow("Unable to connect to database [%s]", sqlite3_errstr(mStatus)); SqThrowF("Unable to connect to database [%s]", sqlite3_errstr(mStatus));
// Unable to proceed
return;
} }
// Let's save the specified information // Let's save the specified information
mName.assign(name); mName.assign(name);
@ -142,11 +153,7 @@ Int32 ConnHnd::Handle::Flush(Uint32 num, Object & env, Function & func)
QueryList::iterator end = mQueue.begin() + num; QueryList::iterator end = mQueue.begin() + num;
// Attempt to begin the flush transaction // Attempt to begin the flush transaction
if ((mStatus = sqlite3_exec(mPtr, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK) if ((mStatus = sqlite3_exec(mPtr, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK)
{ SqThrowF("Unable to begin transaction [%s]", sqlite3_errmsg(mPtr));
_SqMod->SqThrow("Unable to begin transaction [%s]", sqlite3_errmsg(mPtr));
// Unable to proceed
return -1;
}
// Process all queries within range of selection // Process all queries within range of selection
for (; itr != end; ++itr) for (; itr != end; ++itr)
{ {
@ -175,10 +182,10 @@ Int32 ConnHnd::Handle::Flush(Uint32 num, Object & env, Function & func)
return sqlite3_changes(mPtr); return sqlite3_changes(mPtr);
// Attempt to roll back erroneous changes // Attempt to roll back erroneous changes
else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK) else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK)
_SqMod->SqThrow("Unable to rollback transaction [%s]", sqlite3_errmsg(mPtr)); SqThrowF("Unable to rollback transaction [%s]", sqlite3_errmsg(mPtr));
// The transaction failed somehow but we managed to rollback // The transaction failed somehow but we managed to rollback
else else
_SqMod->SqThrow("Unable to commit transaction because [%s]", sqlite3_errmsg(mPtr)); SqThrowF("Unable to commit transaction because [%s]", sqlite3_errmsg(mPtr));
// Operation failed // Operation failed
return -1; return -1;
} }
@ -192,7 +199,7 @@ StmtHnd::Handle::~Handle()
// Are we dealing with a memory leak? Technically shouldn't reach this situation! // Are we dealing with a memory leak? Technically shouldn't reach this situation!
else if (mRef != 0) else if (mRef != 0)
// Should we deal with undefined behavior instead? How bad is one statement left alive? // Should we deal with undefined behavior instead? How bad is one statement left alive?
_SqMod->LogErr("SQLite statement is still referenced."); _SqMod->LogErr("SQLite statement is still referenced (%s)", mQuery.c_str());
else else
{ {
// Attempt to finalize the statement // Attempt to finalize the statement
@ -206,33 +213,25 @@ void StmtHnd::Handle::Create(CSStr query)
{ {
// Make sure a previous statement doesn't exist // Make sure a previous statement doesn't exist
if (mPtr) if (mPtr)
{ SqThrowF("Unable to prepare statement. Statement already prepared");
_SqMod->SqThrow("Unable to prepare statement. Statement already prepared");
// Unable to proceed
return;
}
// Is the specified database connection is valid? // Is the specified database connection is valid?
else if (!mConn) else if (!mConn)
{ SqThrowF("Unable to prepare statement. Invalid connection handle");
_SqMod->SqThrow("Unable to prepare statement. Invalid connection handle");
// Unable to proceed
return;
}
// Save the query string and therefore multiple strlen(...) calls // Save the query string and therefore multiple strlen(...) calls
mQuery.assign(query ? query : _SC("")); mQuery.assign(query ? query : _SC(""));
// Is the specified query string we just saved, valid? // Is the specified query string we just saved, valid?
if (mQuery.empty()) if (mQuery.empty())
_SqMod->SqThrow("Unable to prepare statement. Invalid query string"); SqThrowF("Unable to prepare statement. Invalid query string");
// Attempt to prepare a statement with the specified 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(), else if ((mStatus = sqlite3_prepare_v2(mConn, mQuery.c_str(), (Int32)mQuery.size(),
&mPtr, NULL)) != SQLITE_OK) &mPtr, NULL)) != SQLITE_OK)
{ {
// Clear the query string since it failed // Clear the query string since it failed
mQuery.clear(); 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 // Explicitly make sure the handle is null
mPtr = NULL; mPtr = NULL;
// Now it's safe to throw the error
SqThrowF("Unable to prepare statement [%s]", mConn.ErrMsg());
} }
else else
// Obtain the number of available columns // Obtain the number of available columns
@ -244,7 +243,7 @@ Int32 StmtHnd::Handle::GetColumnIndex(CSStr name)
{ {
// Validate the handle // Validate the handle
if (!mPtr) if (!mPtr)
_SqMod->SqThrow("Invalid SQLite statement"); SqThrowF("Invalid SQLite statement");
// Are the names cached? // Are the names cached?
else if (mIndexes.empty()) else if (mIndexes.empty())
{ {
@ -254,7 +253,7 @@ Int32 StmtHnd::Handle::GetColumnIndex(CSStr name)
CSStr name = (CSStr)sqlite3_column_name(mPtr, i); CSStr name = (CSStr)sqlite3_column_name(mPtr, i);
// Validate the name // Validate the name
if (!name) if (!name)
_SqMod->SqThrow("Unable to retrieve column name for index (%d)", i); SqThrowF("Unable to retrieve column name for index (%d)", i);
// Save it to guarantee the same lifetime as this instance // Save it to guarantee the same lifetime as this instance
else else
mIndexes[name] = i; mIndexes[name] = i;
@ -269,6 +268,12 @@ Int32 StmtHnd::Handle::GetColumnIndex(CSStr name)
return -1; return -1;
} }
// ------------------------------------------------------------------------------------------------
CSStr GetErrStr(Int32 status)
{
return sqlite3_errstr(status);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void SetSoftHeapLimit(Int32 limit) void SetSoftHeapLimit(Int32 limit)
{ {
@ -290,7 +295,7 @@ Object GetMemoryUsage()
_SqMod->PushSLongObject(_SqVM, sqlite3_memory_used()); _SqMod->PushSLongObject(_SqVM, sqlite3_memory_used());
// Obtain the object from the stack // Obtain the object from the stack
Var< Object > inst(_SqVM, -1); Var< Object > inst(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack // Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance // Return the long integer instance
return inst.value; return inst.value;
@ -305,7 +310,7 @@ Object GetMemoryHighwaterMark(bool reset)
_SqMod->PushSLongObject(_SqVM, sqlite3_memory_highwater(reset)); _SqMod->PushSLongObject(_SqVM, sqlite3_memory_highwater(reset));
// Obtain the object from the stack // Obtain the object from the stack
Var< Object > inst(_SqVM, -1); Var< Object > inst(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack // Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top); sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance // Return the long integer instance
return inst.value; return inst.value;
@ -331,7 +336,7 @@ CCStr EscapeStringEx(SQChar spec, CCStr str)
// Validate the specified format specifier // Validate the specified format specifier
if (spec != 'q' && spec != 'Q' && spec != 'w' && spec != 's') if (spec != 'q' && spec != 'Q' && spec != 'w' && spec != 's')
{ {
_SqMod->SqThrow("Unknown format specifier: %c", spec); SqThrowF("Unknown format specifier: %c", spec);
// Default to empty string // Default to empty string
return _SC(""); return _SC("");
} }
@ -367,11 +372,7 @@ CCStr ArrayToQueryColumns(Array & arr)
{ {
// Is the name valid? // Is the name valid?
if (itr->empty()) if (itr->empty())
{ SqThrowF("Invalid column name");
_SqMod->SqThrow("Invalid column name");
// Default to empty string
return _SC("");
}
// Attempt to append the column name to the buffer // Attempt to append the column name to the buffer
sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", itr->c_str()); sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", itr->c_str());
// Add the column name size to the offset // Add the column name size to the offset
@ -403,11 +404,7 @@ CCStr TableToQueryColumns(Table & tbl)
name.assign(itr.getName()); name.assign(itr.getName());
// Is the name valid? // Is the name valid?
if (name.empty()) if (name.empty())
{ SqThrowF("Invalid column name");
_SqMod->SqThrow("Invalid column name");
// Default to empty string
return _SC("");
}
// Attempt to append the column name to the buffer // Attempt to append the column name to the buffer
sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", name.c_str()); sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", name.c_str());
// Add the column name size to the offset // Add the column name size to the offset

View File

@ -38,6 +38,16 @@ class Transaction;
#define SQSQLITE_VERSION_MINOR 0 #define SQSQLITE_VERSION_MINOR 0
#define SQSQLITE_VERSION_PATCH 1 #define SQSQLITE_VERSION_PATCH 1
/* ------------------------------------------------------------------------------------------------
* Retrieve the temporary buffer.
*/
SStr GetTempBuff();
/* ------------------------------------------------------------------------------------------------
* Throw a formatted exception.
*/
void SqThrowF(CSStr str, ...);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* Generate a formatted string. * Generate a formatted string.
*/ */
@ -726,42 +736,47 @@ public:
}; };
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Retrieve the string representation of a certain status code.
*/
CSStr GetErrStr(Int32 status);
/* ------------------------------------------------------------------------------------------------
* Set a specific heap limit.
*/ */
void SetSoftHeapLimit(Int32 limit); void SetSoftHeapLimit(Int32 limit);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Release the specified amount of memory.
*/ */
Int32 ReleaseMemory(Int32 bytes); Int32 ReleaseMemory(Int32 bytes);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Retrieve the current memory usage.
*/ */
Object GetMemoryUsage(); Object GetMemoryUsage();
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Retrieve the memory high watermark.
*/ */
Object GetMemoryHighwaterMark(bool reset); Object GetMemoryHighwaterMark(bool reset);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Retrieve the escaped version of the specified string.
*/ */
CSStr EscapeString(CSStr str); CSStr EscapeString(CSStr str);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Retrieve the escaped version of the specified string using the supplied format specifier.
*/ */
CCStr EscapeStringEx(SQChar spec, CCStr str); CCStr EscapeStringEx(SQChar spec, CCStr str);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Convert the values from the specified array to a list of column names string.
*/ */
CCStr ArrayToQueryColumns(Array & arr); CCStr ArrayToQueryColumns(Array & arr);
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* * Convert the keys from the specified array to a list of column names string.
*/ */
CCStr TableToQueryColumns(Table & tbl); CCStr TableToQueryColumns(Table & tbl);

View File

@ -18,13 +18,11 @@ SQInteger Connection::Typename(HSQUIRRELVM vm)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Connection::Validate() const void Connection::Validate() const
{ {
if (m_Handle) // Is the handle valid?
return true; if (!m_Handle)
// Invalid connection reference SqThrowF("Invalid SQLite connection reference");
_SqMod->SqThrow("Invalid SQLite connection reference");
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -40,12 +38,6 @@ Connection::Connection(CSStr name)
{ {
if (m_Handle.m_Hnd) if (m_Handle.m_Hnd)
m_Handle->Create(name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); m_Handle->Create(name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
// Because Sqrat is majorly stupid and clears the error message
// then does an assert on debug builds thinking the type wasn't registered
// or throws a generic "unknown error" message on release builds
// we have to use this approach
if (Sqrat::Error::Occurred(_SqVM))
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -54,12 +46,6 @@ Connection::Connection(CSStr name, Int32 flags)
{ {
if (m_Handle.m_Hnd) if (m_Handle.m_Hnd)
m_Handle->Create(name, flags, NULL); m_Handle->Create(name, flags, NULL);
// Because Sqrat is majorly stupid and clears the error message
// then does an assert on debug builds thinking the type wasn't registered
// or throws a generic "unknown error" message on release builds
// we have to use this approach
if (Sqrat::Error::Occurred(_SqVM))
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -68,48 +54,38 @@ Connection::Connection(CSStr name, Int32 flags, CSStr vfs)
{ {
if (m_Handle.m_Hnd) if (m_Handle.m_Hnd)
m_Handle->Create(name, flags, vfs); m_Handle->Create(name, flags, vfs);
// Because Sqrat is majorly stupid and clears the error message
// then does an assert on debug builds thinking the type wasn't registered
// or throws a generic "unknown error" message on release builds
// we have to use this approach
if (Sqrat::Error::Occurred(_SqVM))
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Connection::Exec(CSStr str) Int32 Connection::Exec(CSStr str)
{ {
// Validate the handle // Validate the handle
if (Validate() && (m_Handle = sqlite3_exec(m_Handle, str, NULL, NULL, NULL)) != SQLITE_OK) Validate();
_SqMod->SqThrow("Unable to execute query [%s]", m_Handle.ErrMsg()); // Attempt to execute the specified query
if ((m_Handle = sqlite3_exec(m_Handle, str, NULL, NULL, NULL)) != SQLITE_OK)
SqThrowF("Unable to execute query [%s]", m_Handle.ErrMsg());
// Return rows affected by this query // Return rows affected by this query
else
return sqlite3_changes(m_Handle); return sqlite3_changes(m_Handle);
// Operation failed
return -1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Connection::Query(CSStr str) const Object Connection::Query(CSStr str) const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return Object(new Statement(m_Handle, str)); return Object(new Statement(m_Handle, str));
// Request failed
return Object(new Statement());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Connection::Queue(CSStr str) void Connection::Queue(CSStr str)
{ {
// Validate the handle // Validate the handle
if (!Validate()) Validate();
return; // Nothing to commit
// Is there a query to commit? // Is there a query to commit?
else if (IsQueryEmpty(str)) if (IsQueryEmpty(str))
_SqMod->SqThrow("No query to queue"); SqThrowF("No query string to queue");
// Add the specified string to the queue // Add the specified string to the queue
else
m_Handle->mQueue.push_back(str); m_Handle->mQueue.push_back(str);
} }
@ -117,26 +93,21 @@ void Connection::Queue(CSStr str)
bool Connection::IsReadOnly() const bool Connection::IsReadOnly() const
{ {
// Validate the handle // Validate the handle
if (!Validate()) Validate();
return false;
// Request the desired information // Request the desired information
const int result = sqlite3_db_readonly(m_Handle, "main"); const int result = sqlite3_db_readonly(m_Handle, "main");
// Verify the result // Verify the result
if (result == -1) if (result == -1)
_SqMod->SqThrow("'main' is not the name of a database on connection"); SqThrowF("'main' is not the name of a database on connection");
// Return the result // Return the requested information
else
return (result != 1); return (result != 1);
// Inexistent is same as read-only
return true;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Connection::TableExists(CCStr name) const bool Connection::TableExists(CCStr name) const
{ {
// Validate the handle // Validate the handle
if (!Validate()) Validate();
return false;
// Prepare a statement to inspect the master table // Prepare a statement to inspect the master table
Statement stmt(m_Handle, "SELECT count(*) FROM [sqlite_master] WHERE [type]='table' AND [name]=?"); Statement stmt(m_Handle, "SELECT count(*) FROM [sqlite_master] WHERE [type]='table' AND [name]=?");
// Could the statement be created? // Could the statement be created?
@ -156,8 +127,7 @@ bool Connection::TableExists(CCStr name) const
Object Connection::GetLastInsertRowID() const Object Connection::GetLastInsertRowID() const
{ {
// Validate the handle // Validate the handle
if (!Validate()) Validate();
return Object();
// Obtain the initial stack size // Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM); const Int32 top = sq_gettop(_SqVM);
// Push a long integer instance with the requested value on the stack // Push a long integer instance with the requested value on the stack
@ -173,127 +143,108 @@ Object Connection::GetLastInsertRowID() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Connection::SetBusyTimeout(Int32 millis) void Connection::SetBusyTimeout(Int32 millis)
{ {
// Validate the handle and apply requested timeout // Validate the handle
if (Validate() && ((m_Handle = sqlite3_busy_timeout(m_Handle, millis)) != SQLITE_OK)) Validate();
_SqMod->SqThrow("Unable to set busy timeout [%s]", m_Handle.ErrMsg()); // Apply requested timeout
if ((m_Handle = sqlite3_busy_timeout(m_Handle, millis)) != SQLITE_OK)
SqThrowF("Unable to set busy timeout [%s]", m_Handle.ErrMsg());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Connection::GetInfo(Int32 operation, bool highwater, bool reset) Int32 Connection::GetInfo(Int32 operation, bool highwater, bool reset)
{ {
// Don't even bother to continue if there's no valid connection handle // Don't even bother to continue if there's no valid connection handle
if (!Validate()) Validate();
return -1;
// Where to retrieve the information // Where to retrieve the information
Int32 cur_value; Int32 cur_value;
Int32 hiwtr_value; Int32 hiwtr_value;
// Attempt to retrieve the specified information // Attempt to retrieve the specified information
if ((m_Handle = sqlite3_db_status(m_Handle, operation, &cur_value, &hiwtr_value, reset)) != SQLITE_OK) 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()); SqThrowF("Unable to get runtime status information", m_Handle.ErrMsg());
// Return what was requested // Return the high-water value if requested
else if (highwater) else if (highwater)
return hiwtr_value; return hiwtr_value;
else // Return the requested information
return cur_value; return cur_value;
// Request failed
return -1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Connection Connection::CopyToMemory() Connection Connection::CopyToMemory()
{ {
// Validate the handle // Validate the handle
if (!Validate()) Validate();
return Connection();
// Is the database already in memory? // Is the database already in memory?
else if (m_Handle->mMemory) if (m_Handle->mMemory)
{ SqThrowF("The database is already in memory");
_SqMod->SqThrow("The database is already in memory");
// No reason to move it again
return Connection();
}
// Destination database // Destination database
ConnHnd db(_SC("")); ConnHnd db(_SC(""));
// Attempt to open an in-memory database // Attempt to open the in-memory database
db->Create(_SC(":memory:"), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); db->Create(_SC(":memory:"), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
// See if the database could be opened // Clear the temporary buffer
if (!db) GetTempBuff()[0] = 0;
// 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 // Begin a transaction to replicate the schema of origin database
if ((m_Handle = sqlite3_exec(m_Handle, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK) if ((m_Handle = sqlite3_exec(m_Handle, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK)
_SqMod->SqThrow("Unable to begin schema replication [%s]", m_Handle.ErrMsg()); SqThrowF("Unable to begin schema replication [%s]", m_Handle.ErrMsg());
// Attempt to replicate the schema of origin database to the in-memory one // Attempt to replicate the schema of origin database to the in-memory one
else if ((m_Handle = sqlite3_exec(m_Handle, else if ((m_Handle = sqlite3_exec(m_Handle,
"SELECT [sql] FROM [sqlite_master] WHERE [sql] NOT NULL AND [tbl_name] != 'sqlite_sequence'", "SELECT [sql] FROM [sqlite_master] WHERE [sql] NOT NULL AND [tbl_name] != 'sqlite_sequence'",
&Connection::ProcessDDLRow, db->mPtr, NULL)) != SQLITE_OK) &Connection::ProcessDDLRow, db->mPtr, NULL)) != SQLITE_OK)
{ {
// Did the error occurred from the DDL process function? // Did the error occurred from the DDL process function?
if (Sqrat::Error::Occurred(_SqVM)) if (GetTempBuff()[0] != 0)
{
// Obtain the occurred message
String msg(Sqrat::Error::Message(_SqVM));
// Throw the resulted message but also include the point where it failed // Throw the resulted message but also include the point where it failed
_SqMod->SqThrow("Unable to replicate schema [%s]", msg.c_str()); SqThrowF("Unable to replicate schema [%s]", GetTempBuff());
}
// Obtain the message from the connection handle if possible // Obtain the message from the connection handle if possible
else else
_SqMod->SqThrow("Unable to replicate schema [%s]", m_Handle.ErrMsg()); SqThrowF("Unable to replicate schema [%s]", m_Handle.ErrMsg());
} }
// Attempt to commit the changes to the database schema replication // Attempt to commit the changes to the database schema replication
else if ((m_Handle = sqlite3_exec(m_Handle, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK) 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()); SqThrowF("Unable to commit schema replication [%s]", m_Handle.ErrMsg());
// Attempt to attach the origin database to the in-memory one // 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()), else if ((db = sqlite3_exec(db, QFmtStr("ATTACH DATABASE '%q' as origin", m_Handle->mName.c_str()),
NULL, NULL, NULL)) != SQLITE_OK) NULL, NULL, NULL)) != SQLITE_OK)
_SqMod->SqThrow("Unable to attach origin [%s]", db.ErrMsg()); SqThrowF("Unable to attach origin [%s]", db.ErrMsg());
// Begin a transaction to replicate the data of origin database // Begin a transaction to replicate the data of origin database
else if ((db = sqlite3_exec(db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK)) else if ((db = sqlite3_exec(db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK))
_SqMod->SqThrow("Unable to begin data replication [%s]", db.ErrMsg()); SqThrowF("Unable to begin data replication [%s]", db.ErrMsg());
// Attempt to replicate the data of origin database to the in-memory one // 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'", else if ((db = sqlite3_exec(db, "SELECT [name] FROM [origin.sqlite_master] WHERE [type]='table'",
&Connection::ProcessDMLRow, db->mPtr, NULL)) != SQLITE_OK) &Connection::ProcessDMLRow, db->mPtr, NULL)) != SQLITE_OK)
{ {
// Did the error occurred from the DML process function? // Did the error occurred from the DML process function?
if (Sqrat::Error::Occurred(_SqVM)) if (GetTempBuff()[0] != 0)
{ {
// Obtain the occurred message
String msg(Sqrat::Error::Message(_SqVM));
// Throw the resulted message but also include the point where it failed // Throw the resulted message but also include the point where it failed
_SqMod->SqThrow("Unable to replicate data [%s]", msg.c_str()); SqThrowF("Unable to replicate data [%s]", GetTempBuff());
} }
// Obtain the message from the connection handle if possible // Obtain the message from the connection handle if possible
else else
_SqMod->SqThrow("Unable to replicate data [%s]", db.ErrMsg()); SqThrowF("Unable to replicate data [%s]", db.ErrMsg());
} }
// Attempt to commit the changes to the database data replication // Attempt to commit the changes to the database data replication
else if ((db = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK) 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 // Attempt to rollback changes from the data copy operation
if ((db = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK) if ((db = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK)
_SqMod->SqThrow("Unable to rollback data replication [%s]", db.ErrMsg()); SqThrowF("Unable to rollback data replication [%s]", db.ErrMsg());
// Attempt to detach the disk origin from in-memory database // Attempt to detach the disk origin from in-memory database
else if ((db = sqlite3_exec(db, "DETACH DATABASE origin", NULL, NULL, NULL)) != SQLITE_OK) else if ((db = sqlite3_exec(db, "DETACH DATABASE origin", NULL, NULL, NULL)) != SQLITE_OK)
_SqMod->SqThrow("Unable to detach origin [%s]", db.ErrMsg()); SqThrowF("Unable to detach origin [%s]", db.ErrMsg());
// Operation failed
SqThrowF("Unable to commit data replication [%s]", db.ErrMsg());
} }
// At this point everything went fine and the database instance should be returned // At this point everything went fine and the database instance should be returned
else
return Connection(db); return Connection(db);
// Failed to replicate the database
return Connection();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Connection::CopyToDatabase(Connection & db) void Connection::CopyToDatabase(const Connection & db)
{ {
// Make sure that we have two valid database handles // Make sure that we have two valid database handles
if (Validate() && db.Validate()) Validate();
_SqMod->SqThrow("Invalid database connections"); db.Validate();
// Attempt to take the snapshot and return the result // Attempt to take the snapshot and return the result
else
TakeSnapshot(db.m_Handle); TakeSnapshot(db.m_Handle);
} }
@ -301,27 +252,21 @@ void Connection::CopyToDatabase(Connection & db)
Int32 Connection::Flush(Uint32 num) Int32 Connection::Flush(Uint32 num)
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
{
// We need to supply a null callback // We need to supply a null callback
Object env; Object env;
Function func; Function func;
// Attempt to flush the requested amount of queries // Attempt to flush the requested amount of queries
return m_Handle->Flush(num, env, func); return m_Handle->Flush(num, env, func);
} }
// Request failed
return -1;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 Connection::Flush(Uint32 num, Object & env, Function & func) Int32 Connection::Flush(Uint32 num, Object & env, Function & func)
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Attempt to flush the requested amount of queries // Attempt to flush the requested amount of queries
return m_Handle->Flush(num, env, func); return m_Handle->Flush(num, env, func);
// Request failed
return -1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -340,10 +285,10 @@ int Connection::ProcessDDLRow(void * db, int columns_count, char ** values, char
{ {
// Make sure that exactly one column exists in the result // Make sure that exactly one column exists in the result
if (columns_count != 1) if (columns_count != 1)
_SqMod->SqThrow("Error occurred during DDL: columns != 1"); FmtStr("Error occurred during DDL: columns != 1");
// Execute the sql statement in values[0] in the received database connection // 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) 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)); FmtStr("Error occurred during DDL execution: %s", sqlite3_errmsg((sqlite3 *)db));
else else
// Continue processing // Continue processing
return 0; return 0;
@ -356,7 +301,7 @@ int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char
// Make sure that exactly one column exists in the result // Make sure that exactly one column exists in the result
if (columns_count != 1) if (columns_count != 1)
{ {
_SqMod->SqThrow("Error occurred during DML: columns != 1"); FmtStr("Error occurred during DML: columns != 1");
// Operation aborted // Operation aborted
return -1; return -1;
} }
@ -364,7 +309,7 @@ int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char
char * sql = sqlite3_mprintf("INSERT INTO main.%q SELECT * FROM origin.%q", values[0], values[0]); 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 // Attempt to execute the generated query string on the received database connection
if (sqlite3_exec((sqlite3 *)db, sql, NULL, NULL, NULL) != SQLITE_OK) if (sqlite3_exec((sqlite3 *)db, sql, NULL, NULL, NULL) != SQLITE_OK)
_SqMod->SqThrow("Error occurred during DML execution: %s", sqlite3_errmsg((sqlite3 *)db)); FmtStr("Error occurred during DML execution: %s", sqlite3_errmsg((sqlite3 *)db));
else else
{ {
// Free the generated query string // Free the generated query string
@ -379,19 +324,24 @@ int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Connection::TakeSnapshot(ConnHnd & destination) void Connection::TakeSnapshot(const ConnHnd & destination)
{ {
// Attempt to initialize a backup structure // Attempt to initialize a backup structure
sqlite3_backup * backup = sqlite3_backup_init(destination, "main", m_Handle, "main"); sqlite3_backup * backup = sqlite3_backup_init(destination, "main", m_Handle, "main");
// See if the backup structure could be created // See if the backup structure could be created
if (!backup) if (!backup)
_SqMod->SqThrow("Unable to initialize the backup structure [%s]", destination.ErrMsg()); SqThrowF("Unable to initialize the backup structure [%s]", destination.ErrMsg());
// -1 to copy the entire source database to the destination // -1 to copy the entire source database to the destination
else if ((m_Handle = sqlite3_backup_step(backup, -1)) != SQLITE_DONE) if ((m_Handle = sqlite3_backup_step(backup, -1)) != SQLITE_DONE)
_SqMod->SqThrow("Unable to copy source [%s]", m_Handle.ErrStr()); {
// Finalize the backup structure first
sqlite3_backup_finish(backup);
// Now it's safe to throw the error
SqThrowF("Unable to copy source [%s]", m_Handle.ErrStr());
}
// Clean up resources allocated by sqlite3_backup_init() // Clean up resources allocated by sqlite3_backup_init()
if ((m_Handle = sqlite3_backup_finish(backup)) != SQLITE_OK) if ((m_Handle = sqlite3_backup_finish(backup)) != SQLITE_OK)
_SqMod->SqThrow("Unable to finalize backup [%s]", m_Handle.ErrStr()); SqThrowF("Unable to finalize backup [%s]", m_Handle.ErrStr());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -421,16 +371,16 @@ SQInteger Connection::ExecF(HSQUIRRELVM vm)
// Now we can throw the error message // Now we can throw the error message
return sq_throwerror(vm, "Unable to retrieve the query"); return sq_throwerror(vm, "Unable to retrieve the query");
} }
// Prevent the object from being destroyed once we pop it
Var< Object > obj(vm, -1);
// If the value was converted to a string then pop the string
sq_pop(vm, sq_gettop(vm) - top);
// Attempt to execute the specified 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 ((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 // Generate the error message and throw the resulted string
return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", inst.value->m_Handle.ErrMsg())); 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 // Push the result onto the stack
sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle)); sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle));
} }
@ -492,6 +442,7 @@ SQInteger Connection::QueueF(HSQUIRRELVM vm)
{ {
// If the value was converted to a string then pop the string // If the value was converted to a string then pop the string
sq_pop(vm, sq_gettop(vm) - top); sq_pop(vm, sq_gettop(vm) - top);
// Now we can throw the error message
return sq_throwerror(vm,"No query to queue"); return sq_throwerror(vm,"No query to queue");
} }
// Attempt to queue the specified query // Attempt to queue the specified query
@ -549,17 +500,18 @@ SQInteger Connection::QueryF(HSQUIRRELVM vm)
// Now we can throw the error message // Now we can throw the error message
return sq_throwerror(vm, "Unable to retrieve the query"); return sq_throwerror(vm, "Unable to retrieve the query");
} }
// Attempt to create a statement with the specified query // Prevent the object from being destroyed once we pop it
ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql)); Var< Object > obj(vm, -1);
// If the value was converted to a string then pop the string // If the value was converted to a string then pop the string
sq_pop(vm, sq_gettop(vm) - top); sq_pop(vm, sq_gettop(vm) - top);
// See if any errors occured // Attempt to create a statement with the specified query
if (Sqrat::Error::Occurred(vm)) try
{ {
// Obtain the error message from sqrat ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql));
String msg = Sqrat::Error::Message(vm); }
// Throw the error message further down the line catch (const Sqrat::Exception & e)
return sq_throwerror(vm, msg.c_str()); {
return sq_throwerror(vm, e.Message().c_str());
} }
} }
// Do we have enough values to call the format function? // Do we have enough values to call the format function?
@ -573,14 +525,13 @@ SQInteger Connection::QueryF(HSQUIRRELVM vm)
if (SQ_FAILED(ret)) if (SQ_FAILED(ret))
return ret; return ret;
// Attempt to create a statement with the specified query // Attempt to create a statement with the specified query
ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql)); try
// See if any errors occured
if (Sqrat::Error::Occurred(vm))
{ {
// Obtain the error message from sqrat ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql));
String msg = Sqrat::Error::Message(vm); }
// Throw the error message further down the line catch (const Sqrat::Exception & e)
return sq_throwerror(vm, msg.c_str()); {
return sq_throwerror(vm, e.Message().c_str());
} }
} }
// All methods of retrieving the message value failed // All methods of retrieving the message value failed

View File

@ -23,7 +23,7 @@ protected:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the document reference and throw an error if invalid. * Validate the document reference and throw an error if invalid.
*/ */
bool Validate() const; void Validate() const;
private: private:
@ -138,10 +138,9 @@ public:
CSStr ToString() const CSStr ToString() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mName.c_str(); return m_Handle->mName.c_str();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -187,10 +186,9 @@ public:
Int32 GetStatus() const Int32 GetStatus() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mStatus; return m_Handle->mStatus;
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -199,10 +197,9 @@ public:
Int32 GetFlags() const Int32 GetFlags() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mFlags; return m_Handle->mFlags;
// Request failed
return 0;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -211,10 +208,9 @@ public:
CSStr GetName() const CSStr GetName() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mName.c_str(); return m_Handle->mName.c_str();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -223,10 +219,9 @@ public:
CSStr GetVFS() const CSStr GetVFS() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mVFS.c_str(); return m_Handle->mVFS.c_str();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -234,9 +229,10 @@ public:
*/ */
Int32 GetErrorCode() const Int32 GetErrorCode() const
{ {
if (Validate()) // Validate the handle
Validate();
// Return the requested information
return m_Handle.ErrNo(); return m_Handle.ErrNo();
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -244,9 +240,10 @@ public:
*/ */
Int32 GetExtendedErrorCode() const Int32 GetExtendedErrorCode() const
{ {
if (Validate()) // Validate the handle
Validate();
// Return the requested information
return m_Handle.ExErrNo(); return m_Handle.ExErrNo();
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -254,9 +251,10 @@ public:
*/ */
CSStr GetErrStr() const CSStr GetErrStr() const
{ {
if (Validate()) // Validate the handle
Validate();
// Return the requested information
return m_Handle.ErrStr(); return m_Handle.ErrStr();
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -264,9 +262,10 @@ public:
*/ */
CSStr GetErrMsg() const CSStr GetErrMsg() const
{ {
if (Validate()) // Validate the handle
Validate();
// Return the requested information
return m_Handle.ErrMsg(); return m_Handle.ErrMsg();
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -326,11 +325,10 @@ public:
*/ */
bool GetAutoCommit() const bool GetAutoCommit() const
{ {
// Request failed // Validate the handle
if (Validate()) Validate();
// Return the requested information
return sqlite3_get_autocommit(m_Handle); return sqlite3_get_autocommit(m_Handle);
// Request failed
return false;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -345,10 +343,9 @@ public:
Int32 GetChanges() const Int32 GetChanges() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return sqlite3_changes(m_Handle); return sqlite3_changes(m_Handle);
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -358,10 +355,9 @@ public:
Int32 GetTotalChanges() const Int32 GetTotalChanges() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return sqlite3_total_changes(m_Handle); return sqlite3_total_changes(m_Handle);
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -370,10 +366,9 @@ public:
bool GetTracing() const bool GetTracing() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mTrace; return m_Handle->mTrace;
// Request failed
return false;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -381,8 +376,10 @@ public:
*/ */
void SetTracing(bool toggle) void SetTracing(bool toggle)
{ {
// Validate the handle and check whether changes are necessary // Validate the handle
if (!Validate() || m_Handle->mTrace == toggle) Validate();
// Check whether changes are necessary
if (m_Handle->mTrace == toggle)
return; /* No point in proceeding */ return; /* No point in proceeding */
// Do we have to disable it? // Do we have to disable it?
else if (m_Handle->mTrace) else if (m_Handle->mTrace)
@ -398,10 +395,9 @@ public:
bool GetProfiling() const bool GetProfiling() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mProfile; return m_Handle->mProfile;
// Request failed
return false;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -409,8 +405,10 @@ public:
*/ */
void SetProfiling(bool toggle) void SetProfiling(bool toggle)
{ {
// Validate the handle and check whether changes are necessary // Validate the handle
if (!Validate() || m_Handle->mProfile == toggle) Validate();
// Check whether changes are necessary
if (m_Handle->mProfile == toggle)
return; /* No point in proceeding */ return; /* No point in proceeding */
// Do we have to disable it? // Do we have to disable it?
else if (m_Handle->mProfile) else if (m_Handle->mProfile)
@ -431,7 +429,8 @@ public:
void InterruptOperation() const void InterruptOperation() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Perform the requested action
sqlite3_interrupt(m_Handle); sqlite3_interrupt(m_Handle);
} }
@ -441,7 +440,8 @@ public:
void ReleaseMemory() const void ReleaseMemory() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Perform the requested action
sqlite3_db_release_memory(m_Handle); sqlite3_db_release_memory(m_Handle);
} }
@ -453,7 +453,7 @@ public:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Takes a snapshot of a database which is located in memory and saves it to a database file. * Takes a snapshot of a database which is located in memory and saves it to a database file.
*/ */
void CopyToDatabase(Connection & db); void CopyToDatabase(const Connection & db);
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Returns internal runtime status information associated with the current database connection. * Returns internal runtime status information associated with the current database connection.
@ -482,10 +482,9 @@ public:
Uint32 QueueSize() const Uint32 QueueSize() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return (Uint32)m_Handle->mQueue.size(); return (Uint32)m_Handle->mQueue.size();
// Request failed
return 0;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -494,7 +493,8 @@ public:
void ClearQueue() const void ClearQueue() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
m_Handle->mQueue.clear(); m_Handle->mQueue.clear();
} }
@ -504,7 +504,9 @@ public:
void PopQueue() const void PopQueue() const
{ {
// Validate the handle // Validate the handle
if (Validate() && !m_Handle->mQueue.empty()) Validate();
// Perform the requested action
if (!m_Handle->mQueue.empty())
m_Handle->mQueue.pop_back(); m_Handle->mQueue.pop_back();
} }
@ -514,10 +516,9 @@ public:
Int32 Flush() Int32 Flush()
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return Flush(m_Handle->mQueue.size()); return Flush(m_Handle->mQueue.size());
// Request failed
return 0;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -570,7 +571,7 @@ protected:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Takes and saves a snapshot of the memory database in a file. * Takes and saves a snapshot of the memory database in a file.
*/ */
void TakeSnapshot(ConnHnd & destination); void TakeSnapshot(const ConnHnd & destination);
}; };
} // Namespace:: SqMod } // Namespace:: SqMod

View File

@ -325,6 +325,7 @@ void RegisterAPI(HSQUIRRELVM vm)
.Func(_SC("Release"), &Column::Release) .Func(_SC("Release"), &Column::Release)
); );
sqlns.Func(_SC("GetErrStr"), &GetErrStr);
sqlns.Func(_SC("SetSoftHeapLimit"), &SetSoftHeapLimit); sqlns.Func(_SC("SetSoftHeapLimit"), &SetSoftHeapLimit);
sqlns.Func(_SC("ReleaseMemory"), &ReleaseMemory); sqlns.Func(_SC("ReleaseMemory"), &ReleaseMemory);
sqlns.Func(_SC("MemoryUsage"), &GetMemoryUsage); sqlns.Func(_SC("MemoryUsage"), &GetMemoryUsage);

File diff suppressed because it is too large Load Diff

View File

@ -27,17 +27,17 @@ protected:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the statement reference and throw an error if invalid. * Validate the statement reference and throw an error if invalid.
*/ */
bool Validate() const; void Validate() const;
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the statement reference and index, and throw an error if they're invalid. * Validate the statement reference and index, and throw an error if they're invalid.
*/ */
bool ValidateIndex(Int32 idx) const; void ValidateIndex(Int32 idx) const;
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Validate the statement reference and row, and throw an error if they're invalid. * Validate the statement reference and row, and throw an error if they're invalid.
*/ */
bool RowAvailable() const; void ValidateRow() const;
public: public:
@ -196,10 +196,9 @@ public:
Int32 GetStatus() const Int32 GetStatus() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mStatus; return m_Handle->mStatus;
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -208,10 +207,9 @@ public:
Int32 GetErrorCode() const Int32 GetErrorCode() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle.ErrNo(); return m_Handle.ErrNo();
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -220,10 +218,9 @@ public:
Int32 GetExtendedErrorCode() const Int32 GetExtendedErrorCode() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle.ExErrNo(); return m_Handle.ExErrNo();
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -232,10 +229,9 @@ public:
CSStr GetErrStr() const CSStr GetErrStr() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle.ErrStr(); return m_Handle.ErrStr();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -244,10 +240,9 @@ public:
CSStr GetErrMsg() const CSStr GetErrMsg() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle.ErrMsg(); return m_Handle.ErrMsg();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -256,10 +251,9 @@ public:
Int32 GetColumns() const Int32 GetColumns() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mColumns; return m_Handle->mColumns;
// Request failed
return -1;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -268,10 +262,9 @@ public:
CSStr GetQuery() const CSStr GetQuery() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mQuery.c_str(); return m_Handle->mQuery.c_str();
// Request failed
return _SC("");
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -280,10 +273,9 @@ public:
bool GetGood() const bool GetGood() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mGood; return m_Handle->mGood;
// Request failed
return false;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
@ -292,10 +284,9 @@ public:
bool GetDone() const bool GetDone() const
{ {
// Validate the handle // Validate the handle
if (Validate()) Validate();
// Return the requested information
return m_Handle->mDone; return m_Handle->mDone;
// Request failed
return false;
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------