1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00
SqMod/modules/sqlite/Statement.cpp
Sandu Liviu Catalin a867bfd84d Fixed various issues with Sqrat thinking the type wasn't registered if an error is thrown in the constructor.
Fixed asserts in connection and statement handles to check the correct property.
Switched various methods to return objects instead of direct types.
Various other fixes and improvements on the SQLite module.
2016-02-27 13:51:14 +02:00

1153 lines
39 KiB
C++

// ------------------------------------------------------------------------------------------------
#include "Statement.hpp"
#include "Connection.hpp"
#include "Column.hpp"
#include "Module.hpp"
// ------------------------------------------------------------------------------------------------
#include <sqrat.h>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
CSStr Statement::s_BadParamI = "Unable to bind [%s] parameter at (%d) because [%s]";
CSStr Statement::s_BadParamS = "Unable to bind [%s] parameter at (%s:%d) because [%s]";
// ------------------------------------------------------------------------------------------------
SQInteger Statement::Typename(HSQUIRRELVM vm)
{
static SQChar name[] = _SC("SqSQLiteStatement");
sq_pushstring(vm, name, sizeof(name));
return 1;
}
// ------------------------------------------------------------------------------------------------
bool Statement::Validate() const
{
if (m_Handle)
return true;
// Invalid statement reference
_SqMod->SqThrow("Invalid SQLite statement reference");
return false;
}
// ------------------------------------------------------------------------------------------------
bool Statement::ValidateIndex(Int32 idx) const
{
// Validate the handle
if (!m_Handle)
_SqMod->SqThrow("Invalid SQLite statement reference");
// Is the specified index in range?
else if (!m_Handle->CheckIndex(idx))
_SqMod->SqThrow("Index out of range: %d", idx);
// Requirements satisfied
else
return true;
// Should not proceed further
return false;
}
// ------------------------------------------------------------------------------------------------
bool Statement::RowAvailable() const
{
// Validate the handle
if (!m_Handle)
_SqMod->SqThrow("Invalid SQLite statement reference");
// Do we have any rows available?
else if (!m_Handle->mGood)
_SqMod->SqThrow("No row available");
// Requirements satisfied
else
return true;
// Need to reset the statement
return false;
}
// ------------------------------------------------------------------------------------------------
Statement::Statement()
: m_Handle()
{
/* ... */
}
// ------------------------------------------------------------------------------------------------
Statement::Statement(const ConnHnd & connection, CSStr query)
: m_Handle(connection)
{
if (m_Handle.m_Hnd)
m_Handle->Create(query);
else
_SqMod->SqThrow("Unable to create the statement reference");
// 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());
}
// ------------------------------------------------------------------------------------------------
Statement::Statement(const Connection & connection, CSStr query)
: m_Handle(connection.GetHandle())
{
// Validate the statement handle
if (m_Handle.m_Hnd)
m_Handle->Create(query);
else
_SqMod->SqThrow("Unable to create the statement reference");
// 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());
}
// ------------------------------------------------------------------------------------------------
Object Statement::GetConnection() const
{
// Validate the handle
if (Validate())
return Object(new Connection(m_Handle->mConn));
// Request failed
return Object(new Connection());
}
// ------------------------------------------------------------------------------------------------
void Statement::Reset()
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Specify that we don't have a row available and we haven't finished stepping
m_Handle->mGood = false;
m_Handle->mDone = false;
// Attempt to reset the statement to it's initial state
m_Handle = sqlite3_reset(m_Handle);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow("Unable to reset statement [%s]", m_Handle.ErrStr());
}
// ------------------------------------------------------------------------------------------------
void Statement::Clear()
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Specify that we don't have a row available and we haven't finished stepping
m_Handle->mGood = false;
m_Handle->mDone = false;
// Attempt to clear the statement
m_Handle = sqlite3_clear_bindings(m_Handle);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow("Unable to clear statement [%s]", m_Handle.ErrStr());
}
// ------------------------------------------------------------------------------------------------
Int32 Statement::Exec()
{
// Validate the handle
if (!Validate())
return -1; // Unable to proceed
// Did we reset first?
else if (m_Handle->mDone)
{
_SqMod->SqThrow("Execute without resetting first");
// Unable to proceed
return -1;
}
// Attempt to step the statement
m_Handle = sqlite3_step(m_Handle);
// Have we finished stepping?
if (m_Handle == SQLITE_DONE)
{
// Specify that we don't have row available and we finished stepping
m_Handle->mGood = false;
m_Handle->mDone = true;
// Return the changes made by this statement
return sqlite3_changes(m_Handle->mConn);
}
// Specify that we don't have any row and we haven't finished stepping
m_Handle->mGood = false;
m_Handle->mDone = false;
// Inspect the result
switch (m_Handle->mStatus)
{
// We don't expect any rows to be returned in this case!
case SQLITE_ROW: _SqMod->SqThrow("Results were found");
case SQLITE_BUSY: _SqMod->SqThrow("Database was busy");
case SQLITE_ERROR: _SqMod->SqThrow("Runtime error occurred");
case SQLITE_MISUSE: _SqMod->SqThrow("Statement misuse");
default: _SqMod->SqThrow("Unknown failure");
}
// Operation failed
return -1;
}
// ------------------------------------------------------------------------------------------------
bool Statement::Step()
{
// Validate the handle
if (!Validate())
return false; // Unable to proceed
// Did we reset first?
else if (m_Handle->mDone)
{
_SqMod->SqThrow("Stepping without resetting first");
// Unable to proceed
return false;
}
// Attempt to step the statement
m_Handle = sqlite3_step(m_Handle);
// Do we have a row available?
if (m_Handle == SQLITE_ROW)
{
// Specify that we have a row available
return (m_Handle->mGood = true);
}
// Have we finished stepping?
else if (m_Handle == SQLITE_DONE)
{
// Specify that we finished stepping
m_Handle->mDone = true;
// Specify that we don't have a row available
return (m_Handle->mGood = false);
}
// Specify that we don't have any row and we haven't finished stepping
m_Handle->mGood = false;
m_Handle->mDone = false;
// Inspect the result
switch (m_Handle->mStatus)
{
case SQLITE_BUSY: _SqMod->SqThrow("Database was busy");
case SQLITE_ERROR: _SqMod->SqThrow("Runtime error occurred");
case SQLITE_MISUSE: _SqMod->SqThrow("Statement misuse");
default: _SqMod->SqThrow("Unknown failure");
}
// Operation failed
return false;
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindA(Int32 idx, Array & arr)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Know when to stop trying to bind values
const Int32 max = sqlite3_bind_parameter_count(m_Handle);
// Make sure that we are at least in bounds
if (idx >= max)
_SqMod->SqThrow("Parameter index out of range");
// Should we clear onward parameters?
else if (arr.Length() <= 0)
{
// Clear all parameters after the specified index
for (Int32 idx = arr.Length(); idx < max; ++idx)
sqlite3_bind_null(m_Handle, idx);
}
// Attempt to retrieve and bind the specified values
else
{
// Allocated memory for the specified amount of values
std::vector< Object > values(arr.Length());
// Attempt to retrieve the specified values
arr.GetArray< Object >(&values[0], values.size());
// Obtain the start of the object list
std::vector< Object >::iterator itr = values.begin();
// Attempt to bind each specified element individually
for (; itr != values.end() && idx < max; ++itr)
IndexBind(idx++, *itr);
}
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindI(Int32 idx, Int32 value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "int", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindL(Int32 idx, Object & value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
else
m_Handle = SQLITE_OK;
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Push the specified object onto the stack
Var< Object & >::push(_SqVM, value);
// The resulted long integer value
Int64 longint = 0;
// Attempt to get the numeric value inside the specified object
if (SQ_FAILED(_SqMod->GetSLongValue(_SqVM, -1, &longint)))
_SqMod->SqThrow("Invalid long integer specified");
// Attempt to bind the specified value
else
m_Handle = sqlite3_bind_int(m_Handle, idx, longint);
// Remove an pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "long", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindV(Int32 idx, SQInteger value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
#ifdef _SQ64
m_Handle = sqlite3_bind_int64(m_Handle, idx, value);
#else
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
#endif // _SQ64
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "value", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindF(Int32 idx, SQFloat value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
m_Handle = sqlite3_bind_double(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "float", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindS(Int32 idx, CSStr value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
m_Handle = sqlite3_bind_text(m_Handle, idx, value, -1, SQLITE_TRANSIENT);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "string", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindB(Int32 idx, bool value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "boolean", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBindN(Int32 idx)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to bind the specified value
m_Handle = sqlite3_bind_null(m_Handle, idx);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "null", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindT(Table & tbl)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Should we clear the all the parameters?
else if (tbl.GetSize() <= 0)
Clear();
// Attempt to assign the specified parameter values
else
{
Table::iterator itr;
Object val;
// Process each element until _next returns null
while (tbl.Next(itr))
{
// Get the value object
val = Object(itr.getValue());
// Bind it to the associated key
NameBind(itr.getName(), val);
}
}
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindI(CSStr name, Int32 value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "int", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindL(CSStr name, Object & value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
else
m_Handle = SQLITE_OK;
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Push the specified object onto the stack
Var< Object & >::push(_SqVM, value);
// The resulted long integer value
Uint64 longint = 0;
// Attempt to get the numeric value inside the specified object
if (SQ_FAILED(_SqMod->GetULongValue(_SqVM, -1, &longint)))
_SqMod->SqThrow("Unknown long integer specified");
// Attempt to bind the specified value
else
m_Handle = sqlite3_bind_int(m_Handle, idx, longint);
// Remove an pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "long", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindV(CSStr name, SQInteger value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
#ifdef _SQ64
m_Handle = sqlite3_bind_int64(m_Handle, idx, value);
#else
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
#endif // _SQ64
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "value", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindF(CSStr name, SQFloat value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
m_Handle = sqlite3_bind_double(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "float", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindS(CSStr name, CSStr value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
m_Handle = sqlite3_bind_text(m_Handle, idx, value, -1, SQLITE_TRANSIENT);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "string", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindB(CSStr name, bool value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
m_Handle = sqlite3_bind_int(m_Handle, idx, value);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "boolean", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBindN(CSStr name)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to bind the specified value
m_Handle = sqlite3_bind_null(m_Handle, idx);
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "null", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::IndexBind(Int32 idx, Object & value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
else
m_Handle = SQLITE_OK;
// Attempt to identify the specified type
switch (value.GetType())
{
// Is this an integer?
case OT_INTEGER:
#ifdef _SQ64
m_Handle = sqlite3_bind_int64(m_Handle, idx, value.Cast< SQInteger >());
#else
m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< SQInteger >());
#endif
break;
// Is this a float?
case OT_FLOAT:
m_Handle = sqlite3_bind_double(m_Handle, idx, value.Cast< SQFloat >());
break;
// Is this a boolean?
case OT_BOOL:
m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< bool >());
break;
// Is this a string?
case OT_STRING:
{
CSStr str = value.Cast< CSStr >();
m_Handle = sqlite3_bind_text(m_Handle, idx, str, -1, SQLITE_TRANSIENT);
} break;
// Is this a null value?
case OT_NULL:
m_Handle = sqlite3_bind_null(m_Handle, idx);
break;
// Unsupported type
default:
{
_SqMod->SqThrow("Attempting to bind unknown value type (%d)", idx);
// Unable to proceed
return;
}
}
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamI, "auto", idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
void Statement::NameBind(CSStr name, Object & value)
{
// Validate the handle
if (!Validate())
return; // Unable to proceed
// Attempt to obtain the index of the specified parameter name
const Int32 idx = sqlite3_bind_parameter_index(m_Handle, name);
// Validate the obtained index
if (!idx)
{
_SqMod->SqThrow("Unknown parameter named (%s)", name);
// Unable to proceed
return;
}
// Attempt to identify the specified type
switch (value.GetType())
{
// Is this an integer?
case OT_INTEGER:
#ifdef _SQ64
m_Handle = sqlite3_bind_int64(m_Handle, idx, value.Cast< SQInteger >());
#else
m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< SQInteger >());
#endif
break;
// Is this a floating point?
case OT_FLOAT:
m_Handle = sqlite3_bind_double(m_Handle, idx, value.Cast< SQFloat >());
break;
// Is this a boolean?
case OT_BOOL:
m_Handle = sqlite3_bind_int(m_Handle, idx, value.Cast< bool >());
break;
// Is this a string?
case OT_STRING:
{
CSStr str = value.Cast< CSStr >();
m_Handle = sqlite3_bind_text(m_Handle, idx, str, -1, SQLITE_TRANSIENT);
} break;
// Is this a null value?
case OT_NULL:
m_Handle = sqlite3_bind_null(m_Handle, idx);
break;
// Unsupported type
default:
{
_SqMod->SqThrow("Attempting to bind unknown value type (%s:%d)", name, idx);
// Unable to proceed
return;
}
}
// Validate the result
if (m_Handle != SQLITE_OK)
_SqMod->SqThrow(s_BadParamS, "auto", name, idx, m_Handle.ErrMsg());
}
// ------------------------------------------------------------------------------------------------
Object Statement::FetchColumnIndex(Int32 idx) const
{
// Validate the handle and row
if (!RowAvailable())
return Object(); // Unable to proceed
// Is the specified index valid?
else if (!m_Handle->CheckIndex(idx))
{
_SqMod->SqThrow("Index out of range: %d", idx);
// Unable to proceed
return Object();
}
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Identify which type of value must be pushed on the stack
switch (sqlite3_column_type(m_Handle, idx))
{
// Is this a null value?
case SQLITE_NULL:
sq_pushnull(_SqVM);
break;
// Is this an integer?
case SQLITE_INTEGER:
#ifdef _SQ64
sq_pushinteger(_SqVM, sqlite3_column_int64(m_Handle, idx));
#else
sq_pushinteger(_SqVM, sqlite3_column_int(m_Handle, idx));
#endif
// Is this a floating point?
case SQLITE_FLOAT:
sq_pushfloat(_SqVM, sqlite3_column_double(m_Handle, idx));
// Is this a string?
case SQLITE_TEXT:
sq_pushstring(_SqVM, (CSStr)sqlite3_column_text(m_Handle, idx),
sqlite3_column_bytes(m_Handle, idx));
// Is this raw data?
case SQLITE_BLOB:
{
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Handle, idx);
// Allocate a blob of the same size
SQUserPointer p = sqstd_createblob(_SqVM, sz);
// Obtain a pointer to the data
const void * b = sqlite3_column_blob(m_Handle, idx);
// Could the memory blob be allocated?
if (!p)
{
_SqMod->SqThrow("Unable to allocate space for column blob value");
// Default to null
sq_pushnull(_SqVM);
}
// Is there any data to read?
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now throw the error
_SqMod->SqThrow("Unable to read data from column blob value");
// Default to null
sq_pushnull(_SqVM);
}
// Copy the data into the memory blob
else
memcpy(p, b, sz);
}
// Unknown type
default:
{
_SqMod->SqThrow("Unknown value to fetch at index: %d", idx);
// Default to null
sq_pushnull(_SqVM);
}
}
// Obtain the object with the value from the stack
Var< Object > obj(_SqVM, -1);
// Remove an pushed values (if any) to restore the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now return the obtained object
return obj.value;
}
// ------------------------------------------------------------------------------------------------
Object Statement::FetchColumnName(CSStr name) const
{
// Validate the handle and row
if (RowAvailable())
return FetchColumnIndex(m_Handle->GetColumnIndex(name));
// Request failed
return Object();
}
// ------------------------------------------------------------------------------------------------
Array Statement::FetchArray() const
{
// Validate the handle
if (Validate())
return FetchArray(0, m_Handle->mColumns-1);
// Request failed
return Array();
}
// ------------------------------------------------------------------------------------------------
Array Statement::FetchArray(Int32 min) const
{
// Validate the handle
if (Validate())
return FetchArray(min, m_Handle->mColumns-1);
// Request failed
return Array();
}
// ------------------------------------------------------------------------------------------------
Array Statement::FetchArray(Int32 min, Int32 max) const
{
// Validate the handle
if (!RowAvailable())
return Array(); // Unable to proceed
// Was there anything selected?
else if (min == max)
return Array(); // Nothing to retrieve
// Is the minimum actually the minimum?
else if (min > max)
{
_SqMod->SqThrow("Minimum is higher than maximum");
// Unable to proceed
return Array();
}
// Is the minimum in range>
else if (!m_Handle->CheckIndex(min))
{
_SqMod->SqThrow("Minimum is out of range");
// Unable to proceed
return Array();
}
// Is the maximum in range?
else if (!m_Handle->CheckIndex(max))
{
_SqMod->SqThrow("Maximum is out of range");
// Unable to proceed
return Array();
}
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Allocate an array large enough to hold the values from selected columns
Array arr(_SqVM, max-min);
// Process the range of selected columns
for (Int32 elem = 0, idx = min; idx < max; ++elem, ++idx)
{
// Identify the type of value that must be assigned
switch (sqlite3_column_type(m_Handle, idx))
{
// Is this a null value?
case SQLITE_NULL:
// Array elements are initialized as null by default so we just leave it like that
break;
// Is this an integer?
case SQLITE_INTEGER:
#ifdef _SQ64
arr.SetValue(elem, sqlite3_column_int64(m_Handle, idx));
#else
arr.SetValue(elem, sqlite3_column_int(m_Handle, idx));
#endif
break;
// Is this a floating point?
case SQLITE_FLOAT:
arr.SetValue(elem, sqlite3_column_double(m_Handle, idx));
break;
// Is this a string?
case SQLITE_TEXT:
arr.SetValue(elem, (CCStr)(sqlite3_column_text(m_Handle, idx)));
break;
// Is this raw data?
case SQLITE_BLOB:
{
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Handle, idx);
// Allocate a blob of the same size
SQUserPointer p = sqstd_createblob(_SqVM, sz);
// Obtain a pointer to the data
const void * b = sqlite3_column_blob(m_Handle, idx);
// Could the memory blob be allocated?
if (!p)
{
_SqMod->SqThrow("Unable to allocate space for column blob value");
// Unable to proceed
return Array();
}
// Is there any data to read?
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now throw the error
_SqMod->SqThrow("Unable to read data from column blob value");
// Unable to proceed
return Array();
}
else
{
// Copy the data into the memory blob
memcpy(p, b, sz);
// Obtain the object from the stack
Var< Object > obj(_SqVM, -1);
// Bind it as an array element
arr.Bind(elem, obj.value);
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
}
}
break;
// Unknown type
default:
_SqMod->SqThrow("Unknown value to fetch at index: %d", idx);
// Unable to proceed
return Array();
}
}
// Return the resulted array
return arr;
}
// ------------------------------------------------------------------------------------------------
Table Statement::FetchTable() const
{
// Validate the handle
if (Validate())
return FetchTable(0, m_Handle->mColumns-1);
// Request failed
return Table();
}
// ------------------------------------------------------------------------------------------------
Table Statement::FetchTable(Int32 min) const
{
// Validate the handle
if (Validate())
return FetchTable(min, m_Handle->mColumns-1);
// Request failed
return Table();
}
// ------------------------------------------------------------------------------------------------
Table Statement::FetchTable(Int32 min, Int32 max) const
{
// Validate the handle
if (!RowAvailable())
return Table(); // Unable to proceed
// Was there anything selected?
else if (min == max)
return Table(); // Nothing to retrieve
// Is the minimum actually the minimum?
else if (min > max)
{
_SqMod->SqThrow("Minimum is higher than maximum");
// Unable to proceed
return Table();
}
// Is the minimum in range>
else if (!m_Handle->CheckIndex(min))
{
_SqMod->SqThrow("Minimum is out of range");
// Unable to proceed
return Table();
}
// Is the maximum in range?
else if (!m_Handle->CheckIndex(max))
{
_SqMod->SqThrow("Maximum is out of range");
// Unable to proceed
return Table();
}
// Obtain the initial stack size
const Int32 top = sq_gettop(_SqVM);
// Allocate an array large enough to hold the values from selected columns
Table tbl(_SqVM);
// Used to bind null values
Object obj;
// Process the range of selected columns
for (Int32 elem = 0, idx = min; idx < max; ++elem, ++idx)
{
//Attempt to obtain the column name
CSStr name = sqlite3_column_name(m_Handle, idx);
// Validate the obtained name
if (!name)
{
_SqMod->SqThrow("Unable to retrieve name of column (%d)", idx);
// Unable to proceed
return Table();
}
// Identify the type of value that must be assigned
switch (sqlite3_column_type(m_Handle, idx))
{
// Is this a null value?
case SQLITE_NULL:
{
// Make sure we don't insert the same instance
obj.Release();
// Now it's safe to insert the value
tbl.SetValue(name, obj);
}
break;
// Is this an integer?
case SQLITE_INTEGER:
#ifdef _SQ64
tbl.SetValue(name, sqlite3_column_int64(m_Handle, idx));
#else
tbl.SetValue(name, sqlite3_column_int(m_Handle, idx));
#endif
break;
// Is this a floating point?
case SQLITE_FLOAT:
tbl.SetValue(name, sqlite3_column_double(m_Handle, idx));
break;
// Is this a string?
case SQLITE_TEXT:
tbl.SetValue(name, (CCStr)(sqlite3_column_text(m_Handle, idx)));
break;
// Is this raw data?
case SQLITE_BLOB:
{
// Obtain the size of the data
const Int32 sz = sqlite3_column_bytes(m_Handle, idx);
// Allocate a blob of the same size
SQUserPointer p = sqstd_createblob(_SqVM, sz);
// Obtain a pointer to the data
const void * b = sqlite3_column_blob(m_Handle, idx);
// Could the memory blob be allocated?
if (!p)
{
_SqMod->SqThrow("Unable to allocate space for column blob value");
// Unable to proceed
return Table();
}
// Is there any data to read?
else if (!b)
{
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
// Now throw the error
_SqMod->SqThrow("Unable to read data from column blob value");
// Unable to proceed
return Table();
}
else
{
// Copy the data into the memory blob
memcpy(p, b, sz);
// Obtain the object from the stack
Var< Object > obj(_SqVM, -1);
// Bind it as a table element
tbl.Bind(name, obj.value);
// Pop the memory blob from the stack
sq_pop(_SqVM, sq_gettop(_SqVM) - top);
}
}
break;
// Unknown type
default:
_SqMod->SqThrow("Unknown value to fetch at index: %d", idx);
// Unable to proceed
return Table();
}
}
// Return the resulted table
return tbl;
}
// ------------------------------------------------------------------------------------------------
bool Statement::CheckIndex(Int32 idx) const
{
// Validate the handle
if (Validate())
return m_Handle->CheckIndex(idx);
// Invalid statement is same as invalid index
return false;
}
// ------------------------------------------------------------------------------------------------
bool Statement::IsColumnNull(Int32 idx) const
{
// Can we make the request?
if (ValidateIndex(idx))
return (sqlite3_column_type(m_Handle, idx) == SQLITE_NULL);
// Request failed
return true;
}
// ------------------------------------------------------------------------------------------------
Int32 Statement::GetColumnIndex(CSStr name) const
{
// Validate the handle
if (Validate())
return m_Handle->GetColumnIndex(name);
// Invalid statement is same as invalid index
return -1;
}
// ------------------------------------------------------------------------------------------------
CSStr Statement::GetColumnName(Int32 idx) const
{
// Can we make the request?
if (ValidateIndex(idx))
return sqlite3_column_name(m_Handle, idx);
// Request failed
return _SC("");
}
// ------------------------------------------------------------------------------------------------
CSStr Statement::GetColumnOriginName(Int32 idx) const
{
#ifdef SQLITE_ENABLE_COLUMN_METADATA
// Can we make the request?
if (ValidateIndex(idx))
return sqlite3_column_origin_name(m_Handle, idx);
#else
SQMOD_UNUSED_VAR(idx);
_SqMod->SqThrow("The module was compiled without this feature");
#endif
// Request failed
return _SC("");
}
// ------------------------------------------------------------------------------------------------
Int32 Statement::GetColumnType(Int32 idx) const
{
// Can we make the request?
if (ValidateIndex(idx))
return sqlite3_column_type(m_Handle, idx);
// Request failed
return -1;
}
// ------------------------------------------------------------------------------------------------
Int32 Statement::GetColumnBytes(Int32 idx) const
{
// Can we make the request?
if (ValidateIndex(idx))
return sqlite3_column_bytes(m_Handle, idx);
// Request failed
return -1;
}
// ------------------------------------------------------------------------------------------------
Object Statement::GetColumnByIndex(Int32 idx) const
{
// Can we make the request?
if (ValidateIndex(idx))
return Object(new Column(m_Handle, idx));
// Request failed
return Object(new Column());
}
// ------------------------------------------------------------------------------------------------
Object Statement::GetColumnByName(CSStr name) const
{
// Validate the handle
if (Validate())
{
// Attempt to obtain the requested column index
const Int32 idx = m_Handle->GetColumnIndex(name);
// Validate the obtained index
if (idx < 0)
_SqMod->SqThrow("Unknown column named (%s)", name);
// Return the requested column
else
return Object(new Column(m_Handle, idx));
}
// Request failed
return Object(new Column());
}
} // Namespace:: SqMod