mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 16:57:16 +01:00
a867bfd84d
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.
1153 lines
39 KiB
C++
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
|