1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00

Implemented RAII when modifying the stack before returning to sqrat inside SQLite module.

Also enabled the latest C++ revision in the project.
Various other fixes and improvements.
This commit is contained in:
Sandu Liviu Catalin 2016-02-28 16:20:33 +02:00
parent 7f0480c966
commit 4c66cfa49d
7 changed files with 190 additions and 97 deletions

View File

@ -362,8 +362,10 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=c++14" />
<Add option="-DSQMOD_PLUGIN_API" />
<Add option="-DSCRAT_USE_EXCEPTIONS" />
<Add option="-DSCRAT_USE_CXX11_OPTIMIZATIONS" />
<Add directory="../modules/sqlite" />
<Add directory="../shared" />
<Add directory="../include" />

View File

@ -98,15 +98,11 @@ Object Column::GetLong() const
// Validate the column and statement row
ValidateRow();
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_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 any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance
return inst.value;
// Get the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------
@ -115,16 +111,12 @@ Object Column::GetString() const
// Validate the column and statement row
ValidateRow();
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_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 any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance
return inst.value;
// Get the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------
@ -142,7 +134,7 @@ Object Column::GetBlob() const
// Validate the column and statement row
ValidateRow();
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_SqVM);
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Stmt, m_Index);
// Allocate a blob of the same size
@ -156,19 +148,15 @@ Object Column::GetBlob() const
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
sq_pop(_SqVM, 1);
// Push a null value instead
sq_pushnull(_SqVM);
}
// Copy the data into the memory blob
else
memcpy(p, b, sz);
// Obtain the object from the stack
Var< Object > inst(_SqVM, -1);
// Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the blob instance
return inst.value;
// Get the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------

View File

@ -10,10 +10,10 @@
#include <sqrat.h>
// ------------------------------------------------------------------------------------------------
static SQChar g_Buffer[4096]; // Common buffer to reduce memory allocations.
namespace SqMod {
// ------------------------------------------------------------------------------------------------
namespace SqMod {
static SQChar g_Buffer[4096]; // Common buffer to reduce memory allocations.
// ------------------------------------------------------------------------------------------------
SStr GetTempBuff()
@ -21,6 +21,12 @@ SStr GetTempBuff()
return g_Buffer;
}
// ------------------------------------------------------------------------------------------------
Uint32 GetTempBuffSize()
{
return sizeof(g_Buffer);
}
// ------------------------------------------------------------------------------------------------
void SqThrowF(CSStr str, ...)
{
@ -84,6 +90,19 @@ bool IsQueryEmpty(CSStr str)
return true;
}
// ------------------------------------------------------------------------------------------------
StackGuard::StackGuard(HSQUIRRELVM vm)
: m_Top(sq_gettop(vm)), m_VM(vm)
{
/* ... */
}
// ------------------------------------------------------------------------------------------------
StackGuard::~StackGuard()
{
sq_pop(m_VM, sq_gettop(m_VM) - m_Top);
}
// ------------------------------------------------------------------------------------------------
ConnHnd::Handle::~Handle()
{
@ -290,30 +309,22 @@ Int32 ReleaseMemory(Int32 bytes)
Object GetMemoryUsage()
{
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_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 any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance
return inst.value;
// Obtain the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------
Object GetMemoryHighwaterMark(bool reset)
{
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_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 any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance
return inst.value;
// Obtain the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------
@ -348,7 +359,7 @@ CCStr EscapeStringEx(SQChar spec, CCStr str)
// Attempt to escape the specified string
sqlite3_snprintf(sizeof(g_Buffer), g_Buffer, fs, str);
// Restore the format specifier
fs[1] = '1';
fs[1] = 'q';
// Return the resulted string
return g_Buffer;
}
@ -389,6 +400,7 @@ CCStr ArrayToQueryColumns(Array & arr)
return g_Buffer;
}
// ------------------------------------------------------------------------------------------------
CCStr TableToQueryColumns(Table & tbl)
{
// Used to know the position of the next column name
@ -404,7 +416,7 @@ CCStr TableToQueryColumns(Table & tbl)
name.assign(itr.getName());
// Is the name valid?
if (name.empty())
SqThrowF("Invalid column name");
SqThrowF("Invalid or empty column name");
// Attempt to append the column name to the buffer
sqlite3_snprintf(sizeof(g_Buffer) - offset, g_Buffer + offset, "[%q], ", name.c_str());
// Add the column name size to the offset

View File

@ -14,6 +14,12 @@
// ------------------------------------------------------------------------------------------------
#include <sqlite3.h>
// ------------------------------------------------------------------------------------------------
extern "C" {
struct SQVM;
typedef struct SQVM* HSQUIRRELVM;
} /*extern "C"*/
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@ -43,6 +49,11 @@ class Transaction;
*/
SStr GetTempBuff();
/* ------------------------------------------------------------------------------------------------
* Retrieve the size of the temporary buffer.
*/
Uint32 GetTempBuffSize();
/* ------------------------------------------------------------------------------------------------
* Throw a formatted exception.
*/
@ -63,6 +74,50 @@ CSStr QFmtStr(CSStr str, ...);
*/
bool IsQueryEmpty(CSStr str);
/* ------------------------------------------------------------------------------------------------
* Implements RAII to restore the VM stack to it's initial size on function exit.
*/
struct StackGuard
{
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
StackGuard(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~StackGuard();
private:
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
StackGuard(const StackGuard &);
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
StackGuard(StackGuard &&);
/* --------------------------------------------------------------------------------------------
* Copy assignment operator.
*/
StackGuard & operator = (const StackGuard &);
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
StackGuard & operator = (StackGuard &&);
private:
// --------------------------------------------------------------------------------------------
Int32 m_Top; /* The top of the stack when this instance was created. */
HSQUIRRELVM m_VM; /* The VM where the stack should be restored. */
};
/* ------------------------------------------------------------------------------------------------
* Manages a reference counted database connection handle.
*/
@ -200,11 +255,19 @@ public:
*/
ConnHnd(const ConnHnd & o)
: m_Hnd(o.m_Hnd)
{
Grab();
}
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
ConnHnd(ConnHnd && o)
: m_Hnd(o.m_Hnd)
{
o.m_Hnd = NULL;
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
@ -227,6 +290,20 @@ public:
return *this;
}
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
ConnHnd & operator = (ConnHnd && o)
{
if (m_Hnd != o.m_Hnd)
{
m_Hnd = o.m_Hnd;
o.m_Hnd = NULL;
}
return *this;
}
/* --------------------------------------------------------------------------------------------
* Status assignment operator.
*/
@ -354,7 +431,6 @@ public:
*/
Counter Count() const
{
assert(m_Hnd);
return m_Hnd ? m_Hnd->mRef : 0;
}
@ -363,8 +439,10 @@ public:
*/
CCStr ErrStr() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr));
// SQLite does it's null pointer validations internally
if (m_Hnd)
return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr));
return _SC("");
}
/* --------------------------------------------------------------------------------------------
@ -372,8 +450,10 @@ public:
*/
CCStr ErrMsg() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return sqlite3_errmsg(m_Hnd->mPtr);
// SQLite does it's null pointer validations internally
if (m_Hnd)
return sqlite3_errmsg(m_Hnd->mPtr);
return _SC("");
}
/* --------------------------------------------------------------------------------------------
@ -381,8 +461,10 @@ public:
*/
Int32 ErrNo() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return sqlite3_errcode(m_Hnd->mPtr);
// SQLite does it's null pointer validations internally
if (m_Hnd)
return sqlite3_errcode(m_Hnd->mPtr);
return SQLITE_NOMEM;
}
/* --------------------------------------------------------------------------------------------
@ -390,8 +472,10 @@ public:
*/
Int32 ExErrNo() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return sqlite3_extended_errcode(m_Hnd->mPtr);
// SQLite does it's null pointer validations internally
if (m_Hnd)
return sqlite3_extended_errcode(m_Hnd->mPtr);
return SQLITE_NOMEM;
}
};
@ -545,6 +629,15 @@ public:
Grab();
}
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
StmtHnd(StmtHnd && o)
: m_Hnd(o.m_Hnd)
{
o.m_Hnd = NULL;
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
@ -567,6 +660,20 @@ public:
return *this;
}
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
StmtHnd & operator = (StmtHnd && o)
{
if (m_Hnd != o.m_Hnd)
{
m_Hnd = o.m_Hnd;
o.m_Hnd = NULL;
}
return *this;
}
/* --------------------------------------------------------------------------------------------
* Status assignment operator.
*/
@ -604,7 +711,7 @@ public:
}
/* --------------------------------------------------------------------------------------------
* Perform an inequality comparison with an integer value status.
* Perform an inequality comparison with an integer status value.
*/
bool operator != (Int32 status) const
{
@ -694,7 +801,6 @@ public:
*/
Counter Count() const
{
assert(m_Hnd);
return m_Hnd ? m_Hnd->mRef : 0;
}
@ -703,8 +809,9 @@ public:
*/
CCStr ErrStr() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return m_Hnd->mConn.ErrStr();
if (m_Hnd)
return m_Hnd->mConn.ErrStr();
return _SC("");
}
/* --------------------------------------------------------------------------------------------
@ -712,8 +819,9 @@ public:
*/
CCStr ErrMsg() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return m_Hnd->mConn.ErrMsg();
if (m_Hnd)
return m_Hnd->mConn.ErrMsg();
return _SC("");
}
/* --------------------------------------------------------------------------------------------
@ -721,8 +829,9 @@ public:
*/
Int32 ErrNo() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return m_Hnd->mConn.ErrNo();
if (m_Hnd)
return m_Hnd->mConn.ErrNo();
return SQLITE_NOMEM;
}
/* --------------------------------------------------------------------------------------------
@ -730,8 +839,9 @@ public:
*/
Int32 ExErrNo() const
{
assert(m_Hnd); // SQLite does it's null pointer validations internally
return m_Hnd->mConn.ExErrNo();
if (m_Hnd)
return m_Hnd->mConn.ExErrNo();
return SQLITE_NOMEM;
}
};

View File

@ -129,15 +129,11 @@ Object Connection::GetLastInsertRowID() const
// Validate the handle
Validate();
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_SqVM);
// Push a long integer instance with the requested value on the stack
_SqMod->PushSLongObject(_SqVM, sqlite3_last_insert_rowid(m_Handle));
// Obtain the object from the stack
Var< Object > inst(_SqVM, -1);
// Removed pushed values (if any)
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Return the long integer instance
return inst.value;
// Get the object from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------

View File

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

View File

@ -245,17 +245,13 @@ void Statement::IndexBindL(Int32 idx, Object & value)
// Validate the handle
Validate();
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_SqVM);
// Push the specified object onto the stack
Var< Object & >::push(_SqVM, value);
// The resulted long integer value
Int64 longint = 0;
// Attempt to get the numeric value inside the specified object
const SQRESULT res = _SqMod->GetSLongValue(_SqVM, -1, &longint);
// Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now it's safe to throw the error if necessary
if (SQ_FAILED(res))
if (SQ_FAILED(_SqMod->GetSLongValue(_SqVM, -1, &longint)))
SqThrowF("Invalid long integer specified");
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, longint);
@ -382,17 +378,13 @@ void Statement::NameBindL(CSStr name, Object & value)
if (!idx)
SqThrowF("Unknown parameter named (%s)", name);
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_SqVM);
// Push the specified object onto the stack
Var< Object & >::push(_SqVM, value);
// The resulted long integer value
Uint64 longint = 0;
// Attempt to get the numeric value inside the specified object
const SQRESULT res = _SqMod->GetULongValue(_SqVM, -1, &longint);
// Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now it's safe to throw the error if necessary
if (SQ_FAILED(res))
if (SQ_FAILED(_SqMod->GetULongValue(_SqVM, -1, &longint)))
SqThrowF("Invalid long integer specified");
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, longint);
@ -590,7 +582,7 @@ Object Statement::FetchColumnIndex(Int32 idx) const
if (!m_Handle->CheckIndex(idx))
SqThrowF("Column index is out of range: %d", idx);
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
const StackGuard sg(_SqVM);
// Identify which type of value must be pushed on the stack
switch (sqlite3_column_type(m_Handle, idx))
{
@ -631,7 +623,7 @@ Object Statement::FetchColumnIndex(Int32 idx) const
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
sq_pop(_SqVM, 1);
// Push a null value instead
sq_pushnull(_SqVM);
}
@ -643,12 +635,8 @@ Object Statement::FetchColumnIndex(Int32 idx) const
default:
SqThrowF("Unknown value to fetch at index: %d", idx);
}
// Obtain the object with the value from the stack
Var< Object > obj(_SqVM, -1);
// Remove any pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now return the obtained object
return obj.value;
// Obtain the object with the value from the stack and return it
return Var< Object >(_SqVM, -1).value;
}
// ------------------------------------------------------------------------------------------------
@ -695,8 +683,6 @@ Array Statement::FetchArray(Int32 min, Int32 max) const
// Is the maximum in range?
else if (!m_Handle->CheckIndex(max))
SqThrowF("Maximum is out of range");
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Allocate an array large enough to hold the values from selected columns
Array arr(_SqVM, max-min);
// Process the range of selected columns
@ -728,6 +714,8 @@ Array Statement::FetchArray(Int32 min, Int32 max) const
// Is this raw data?
case SQLITE_BLOB:
{
// Obtain the initial stack size
const StackGuard sg(_SqVM);
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Handle, idx);
// Allocate a blob of the same size
@ -741,7 +729,7 @@ Array Statement::FetchArray(Int32 min, Int32 max) const
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
sq_pop(_SqVM, 1);
// Push a null value instead
sq_pushnull(_SqVM);
}
@ -750,8 +738,6 @@ Array Statement::FetchArray(Int32 min, Int32 max) const
memcpy(p, b, sz);
// Obtain the object from the stack
Var< Object > obj(_SqVM, -1);
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Bind it as an array element
arr.Bind(elem, obj.value);
} break;
@ -799,8 +785,6 @@ Table Statement::FetchTable(Int32 min, Int32 max) const
// Is the maximum in range?
else if (!m_Handle->CheckIndex(max))
SqThrowF("Maximum is out of range");
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Create a table to hold the selected column values
Table tbl(_SqVM);
// Used to bind null values
@ -843,6 +827,8 @@ Table Statement::FetchTable(Int32 min, Int32 max) const
// Is this raw data?
case SQLITE_BLOB:
{
// Obtain the initial stack size
const StackGuard sg(_SqVM);
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Handle, idx);
// Allocate a blob of the same size
@ -856,7 +842,7 @@ Table Statement::FetchTable(Int32 min, Int32 max) const
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
sq_pop(_SqVM, 1);
// Push a null value instead
sq_pushnull(_SqVM);
}
@ -865,8 +851,6 @@ Table Statement::FetchTable(Int32 min, Int32 max) const
memcpy(p, b, sz);
// Obtain the object from the stack
Var< Object > obj(_SqVM, -1);
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Bind it as a table element
tbl.Bind(name, obj.value);
} break;