mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 08:47:17 +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.
594 lines
24 KiB
C++
594 lines
24 KiB
C++
// ------------------------------------------------------------------------------------------------
|
|
#include "Connection.hpp"
|
|
#include "Statement.hpp"
|
|
#include "Module.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
#include <sqrat.h>
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Connection::Typename(HSQUIRRELVM vm)
|
|
{
|
|
static SQChar name[] = _SC("SqSQLiteConnection");
|
|
sq_pushstring(vm, name, sizeof(name));
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::Validate() const
|
|
{
|
|
if (m_Handle)
|
|
return true;
|
|
// Invalid connection reference
|
|
_SqMod->SqThrow("Invalid SQLite connection reference");
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection()
|
|
: m_Handle()
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(CSStr name)
|
|
: m_Handle(name)
|
|
{
|
|
if (m_Handle.m_Hnd)
|
|
m_Handle->Create(name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
|
// Because Sqrat is majorly stupid and clears the error message
|
|
// then does an assert on debug builds thinking the type wasn't registered
|
|
// or throws a generic "unknown error" message on release builds
|
|
// we have to use this approach
|
|
if (Sqrat::Error::Occurred(_SqVM))
|
|
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(CSStr name, Int32 flags)
|
|
: m_Handle(name)
|
|
{
|
|
if (m_Handle.m_Hnd)
|
|
m_Handle->Create(name, flags, NULL);
|
|
// Because Sqrat is majorly stupid and clears the error message
|
|
// then does an assert on debug builds thinking the type wasn't registered
|
|
// or throws a generic "unknown error" message on release builds
|
|
// we have to use this approach
|
|
if (Sqrat::Error::Occurred(_SqVM))
|
|
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(CSStr name, Int32 flags, CSStr vfs)
|
|
: m_Handle(name)
|
|
{
|
|
if (m_Handle.m_Hnd)
|
|
m_Handle->Create(name, flags, vfs);
|
|
// Because Sqrat is majorly stupid and clears the error message
|
|
// then does an assert on debug builds thinking the type wasn't registered
|
|
// or throws a generic "unknown error" message on release builds
|
|
// we have to use this approach
|
|
if (Sqrat::Error::Occurred(_SqVM))
|
|
_SqMod->LogErr("%s", Sqrat::Error::Message(_SqVM).c_str());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Connection::Exec(CSStr str)
|
|
{
|
|
// Validate the handle
|
|
if (Validate() && (m_Handle = sqlite3_exec(m_Handle, str, NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to execute query [%s]", m_Handle.ErrMsg());
|
|
// Return rows affected by this query
|
|
else
|
|
return sqlite3_changes(m_Handle);
|
|
// Operation failed
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Connection::Query(CSStr str) const
|
|
{
|
|
// Validate the handle
|
|
if (Validate())
|
|
return Object(new Statement(m_Handle, str));
|
|
// Request failed
|
|
return Object(new Statement());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::Queue(CSStr str)
|
|
{
|
|
// Validate the handle
|
|
if (!Validate())
|
|
return; // Nothing to commit
|
|
// Is there a query to commit?
|
|
else if (IsQueryEmpty(str))
|
|
_SqMod->SqThrow("No query to queue");
|
|
// Add the specified string to the queue
|
|
else
|
|
m_Handle->mQueue.push_back(str);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::IsReadOnly() const
|
|
{
|
|
// Validate the handle
|
|
if (!Validate())
|
|
return false;
|
|
// Request the desired information
|
|
const int result = sqlite3_db_readonly(m_Handle, "main");
|
|
// Verify the result
|
|
if (result == -1)
|
|
_SqMod->SqThrow("'main' is not the name of a database on connection");
|
|
// Return the result
|
|
else
|
|
return (result != 1);
|
|
// Inexistent is same as read-only
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::TableExists(CCStr name) const
|
|
{
|
|
// Validate the handle
|
|
if (!Validate())
|
|
return false;
|
|
// Prepare a statement to inspect the master table
|
|
Statement stmt(m_Handle, "SELECT count(*) FROM [sqlite_master] WHERE [type]='table' AND [name]=?");
|
|
// Could the statement be created?
|
|
if (stmt.IsValid())
|
|
{
|
|
// Bind the specified name onto the statement parameter
|
|
stmt.IndexBindS(1, name);
|
|
// Attempt to step the statement and obtain a value
|
|
if (stmt.Step())
|
|
return (sqlite3_column_int(stmt, 0) == 1);
|
|
}
|
|
// Assume it doesn't exist
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Connection::GetLastInsertRowID() const
|
|
{
|
|
// Validate the handle
|
|
if (!Validate())
|
|
return Object();
|
|
// Obtain the initial stack size
|
|
const Int32 top = sq_gettop(_SqVM);
|
|
// Push a long integer instance with the requested value on the stack
|
|
_SqMod->PushSLongObject(_SqVM, sqlite3_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;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::SetBusyTimeout(Int32 millis)
|
|
{
|
|
// Validate the handle and apply requested timeout
|
|
if (Validate() && ((m_Handle = sqlite3_busy_timeout(m_Handle, millis)) != SQLITE_OK))
|
|
_SqMod->SqThrow("Unable to set busy timeout [%s]", m_Handle.ErrMsg());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Connection::GetInfo(Int32 operation, bool highwater, bool reset)
|
|
{
|
|
// Don't even bother to continue if there's no valid connection handle
|
|
if (!Validate())
|
|
return -1;
|
|
// Where to retrieve the information
|
|
Int32 cur_value;
|
|
Int32 hiwtr_value;
|
|
// Attempt to retrieve the specified information
|
|
if((m_Handle = sqlite3_db_status(m_Handle, operation, &cur_value, &hiwtr_value, reset)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to get runtime status information", m_Handle.ErrMsg());
|
|
// Return what was requested
|
|
else if (highwater)
|
|
return hiwtr_value;
|
|
else
|
|
return cur_value;
|
|
// Request failed
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection Connection::CopyToMemory()
|
|
{
|
|
// Validate the handle
|
|
if (!Validate())
|
|
return Connection();
|
|
// Is the database already in memory?
|
|
else if (m_Handle->mMemory)
|
|
{
|
|
_SqMod->SqThrow("The database is already in memory");
|
|
// No reason to move it again
|
|
return Connection();
|
|
}
|
|
// Destination database
|
|
ConnHnd db(_SC(""));
|
|
// Attempt to open an in-memory database
|
|
db->Create(_SC(":memory:"), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
|
// See if the database could be opened
|
|
if (!db)
|
|
// The creation process already generated the error
|
|
return Connection();
|
|
// Clear any previous error (there shouldn't be any but just in case)
|
|
Sqrat::Error::Clear(_SqVM);
|
|
// Begin a transaction to replicate the schema of origin database
|
|
if ((m_Handle = sqlite3_exec(m_Handle, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to begin schema replication [%s]", m_Handle.ErrMsg());
|
|
// Attempt to replicate the schema of origin database to the in-memory one
|
|
else if ((m_Handle = sqlite3_exec(m_Handle,
|
|
"SELECT [sql] FROM [sqlite_master] WHERE [sql] NOT NULL AND [tbl_name] != 'sqlite_sequence'",
|
|
&Connection::ProcessDDLRow, db->mPtr, NULL)) != SQLITE_OK)
|
|
{
|
|
// Did the error occurred from the DDL process function?
|
|
if (Sqrat::Error::Occurred(_SqVM))
|
|
{
|
|
// Obtain the occurred message
|
|
String msg(Sqrat::Error::Message(_SqVM));
|
|
// Throw the resulted message but also include the point where it failed
|
|
_SqMod->SqThrow("Unable to replicate schema [%s]", msg.c_str());
|
|
}
|
|
// Obtain the message from the connection handle if possible
|
|
else
|
|
_SqMod->SqThrow("Unable to replicate schema [%s]", m_Handle.ErrMsg());
|
|
}
|
|
// Attempt to commit the changes to the database schema replication
|
|
else if ((m_Handle = sqlite3_exec(m_Handle, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to commit schema replication [%s]", m_Handle.ErrMsg());
|
|
// Attempt to attach the origin database to the in-memory one
|
|
else if ((db = sqlite3_exec(db, QFmtStr("ATTACH DATABASE '%q' as origin", m_Handle->mName.c_str()),
|
|
NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to attach origin [%s]", db.ErrMsg());
|
|
// Begin a transaction to replicate the data of origin database
|
|
else if ((db = sqlite3_exec(db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK))
|
|
_SqMod->SqThrow("Unable to begin data replication [%s]", db.ErrMsg());
|
|
// Attempt to replicate the data of origin database to the in-memory one
|
|
else if ((db = sqlite3_exec(db, "SELECT [name] FROM [origin.sqlite_master] WHERE [type]='table'",
|
|
&Connection::ProcessDMLRow, db->mPtr, NULL)) != SQLITE_OK)
|
|
{
|
|
// Did the error occurred from the DML process function?
|
|
if (Sqrat::Error::Occurred(_SqVM))
|
|
{
|
|
// Obtain the occurred message
|
|
String msg(Sqrat::Error::Message(_SqVM));
|
|
// Throw the resulted message but also include the point where it failed
|
|
_SqMod->SqThrow("Unable to replicate data [%s]", msg.c_str());
|
|
}
|
|
// Obtain the message from the connection handle if possible
|
|
else
|
|
_SqMod->SqThrow("Unable to replicate data [%s]", db.ErrMsg());
|
|
}
|
|
// Attempt to commit the changes to the database data replication
|
|
else if ((db = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
_SqMod->SqThrow("Unable to commit data replication [%s]", db.ErrMsg());
|
|
// Attempt to rollback changes from the data copy operation
|
|
if ((db = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to rollback data replication [%s]", db.ErrMsg());
|
|
// Attempt to detach the disk origin from in-memory database
|
|
else if ((db = sqlite3_exec(db, "DETACH DATABASE origin", NULL, NULL, NULL)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to detach origin [%s]", db.ErrMsg());
|
|
}
|
|
// At this point everything went fine and the database instance should be returned
|
|
else
|
|
return Connection(db);
|
|
// Failed to replicate the database
|
|
return Connection();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::CopyToDatabase(Connection & db)
|
|
{
|
|
// Make sure that we have two valid database handles
|
|
if (Validate() && db.Validate())
|
|
_SqMod->SqThrow("Invalid database connections");
|
|
// Attempt to take the snapshot and return the result
|
|
else
|
|
TakeSnapshot(db.m_Handle);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Connection::Flush(Uint32 num)
|
|
{
|
|
// Validate the handle
|
|
if (Validate())
|
|
{
|
|
// We need to supply a null callback
|
|
Object env;
|
|
Function func;
|
|
// Attempt to flush the requested amount of queries
|
|
return m_Handle->Flush(num, env, func);
|
|
}
|
|
// Request failed
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Connection::Flush(Uint32 num, Object & env, Function & func)
|
|
{
|
|
// Validate the handle
|
|
if (Validate())
|
|
// Attempt to flush the requested amount of queries
|
|
return m_Handle->Flush(num, env, func);
|
|
// Request failed
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::TraceOutput(void * /*ptr*/, CCStr sql)
|
|
{
|
|
_SqMod->LogInf("SQLite Trace: %s", sql);
|
|
}
|
|
|
|
void Connection::ProfileOutput(void * /*ptr*/, CCStr sql, sqlite3_uint64 time)
|
|
{
|
|
_SqMod->LogInf("SQLite profile (time: %llu): %s", time, sql);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
int Connection::ProcessDDLRow(void * db, int columns_count, char ** values, char ** /*columns*/)
|
|
{
|
|
// Make sure that exactly one column exists in the result
|
|
if (columns_count != 1)
|
|
_SqMod->SqThrow("Error occurred during DDL: columns != 1");
|
|
// Execute the sql statement in values[0] in the received database connection
|
|
else if (sqlite3_exec((sqlite3 *)db, values[0], NULL, NULL, NULL) != SQLITE_OK)
|
|
_SqMod->SqThrow("Error occurred during DDL execution: %s", sqlite3_errmsg((sqlite3 *)db));
|
|
else
|
|
// Continue processing
|
|
return 0;
|
|
// Operation aborted
|
|
return -1;
|
|
}
|
|
|
|
int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char ** /*columns*/)
|
|
{
|
|
// Make sure that exactly one column exists in the result
|
|
if(columns_count != 1)
|
|
{
|
|
_SqMod->SqThrow("Error occurred during DML: columns != 1");
|
|
// Operation aborted
|
|
return -1;
|
|
}
|
|
// Generate the query string with the received values
|
|
char * sql = sqlite3_mprintf("INSERT INTO main.%q SELECT * FROM origin.%q", values[0], values[0]);
|
|
// Attempt to execute the generated query string on the received database connection
|
|
if (sqlite3_exec((sqlite3 *)db, sql, NULL, NULL, NULL) != SQLITE_OK)
|
|
_SqMod->SqThrow("Error occurred during DML execution: %s", sqlite3_errmsg((sqlite3 *)db));
|
|
else
|
|
{
|
|
// Free the generated query string
|
|
sqlite3_free(sql);
|
|
// Continue processing
|
|
return 0;
|
|
}
|
|
// Free the generated query string
|
|
sqlite3_free(sql);
|
|
// Operation aborted
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::TakeSnapshot(ConnHnd & destination)
|
|
{
|
|
// Attempt to initialize a backup structure
|
|
sqlite3_backup * backup = sqlite3_backup_init(destination, "main", m_Handle, "main");
|
|
// See if the backup structure could be created
|
|
if (!backup)
|
|
_SqMod->SqThrow("Unable to initialize the backup structure [%s]", destination.ErrMsg());
|
|
// -1 to copy the entire source database to the destination
|
|
else if ((m_Handle = sqlite3_backup_step(backup, -1)) != SQLITE_DONE)
|
|
_SqMod->SqThrow("Unable to copy source [%s]", m_Handle.ErrStr());
|
|
// Clean up resources allocated by sqlite3_backup_init()
|
|
if ((m_Handle = sqlite3_backup_finish(backup)) != SQLITE_OK)
|
|
_SqMod->SqThrow("Unable to finalize backup [%s]", m_Handle.ErrStr());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Connection::ExecF(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Do we even have enough arguments?
|
|
if (top <= 1)
|
|
return sq_throwerror(vm, "Missing the query string");
|
|
// Obtain the connection instance
|
|
Var< Connection * > inst(vm, 1);
|
|
// Do we have a valid connection instance?
|
|
if (!inst.value)
|
|
return sq_throwerror(vm, "Invalid connection instance");
|
|
// Do we have a valid connection reference?
|
|
else if (!inst.value->m_Handle)
|
|
return sq_throwerror(vm, "Invalid SQLite connection reference");
|
|
// Is the specified message value a string or something convertible to string?
|
|
else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1))))
|
|
{
|
|
CCStr sql = NULL;
|
|
// Attempt to retrieve the string from the stack
|
|
if (SQ_FAILED(sq_getstring(vm, -1, &sql)))
|
|
{
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// Now we can throw the error message
|
|
return sq_throwerror(vm, "Unable to retrieve the query");
|
|
}
|
|
// Attempt to execute the specified query
|
|
if ((inst.value->m_Handle = sqlite3_exec(inst.value->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// Generate the error message and throw the resulted string
|
|
return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", inst.value->m_Handle.ErrMsg()));
|
|
}
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// Push the result onto the stack
|
|
sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle));
|
|
}
|
|
// Do we have enough values to call the format function?
|
|
else if (top > 2)
|
|
{
|
|
SStr sql = NULL;
|
|
SQInteger len = 0;
|
|
// Attempt to generate the specified string format
|
|
SQRESULT ret = sqstd_format(vm, 3, &len, &sql);
|
|
// Did the format failed?
|
|
if (SQ_FAILED(ret))
|
|
return ret;
|
|
// Attempt to execute the resulted query
|
|
if ((inst.value->m_Handle = sqlite3_exec(inst.value->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
// Generate the error message and throw the resulted string
|
|
return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", inst.value->m_Handle.ErrMsg()));
|
|
}
|
|
// Push the result onto the stack
|
|
sq_pushinteger(vm, sqlite3_changes(inst.value->m_Handle));
|
|
}
|
|
// All methods of retrieving the message value failed
|
|
else
|
|
return sq_throwerror(vm, "Unable to extract the query string");
|
|
// At this point we should have a return value on the stack
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Connection::QueueF(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Do we even have enough arguments?
|
|
if (top <= 1)
|
|
return sq_throwerror(vm, "Missing the query string");
|
|
// Obtain the connection instance
|
|
Var< Connection * > inst(vm, 1);
|
|
// Do we have a valid connection instance?
|
|
if (!inst.value)
|
|
return sq_throwerror(vm, "Invalid connection instance");
|
|
// Do we have a valid connection reference?
|
|
else if (!inst.value->m_Handle)
|
|
return sq_throwerror(vm, "Invalid SQLite connection reference");
|
|
// Is the specified message value a string or something convertible to string?
|
|
else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1))))
|
|
{
|
|
CCStr sql = NULL;
|
|
// Attempt to retrieve the string from the stack
|
|
if (SQ_FAILED(sq_getstring(vm, -1, &sql)))
|
|
{
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// Now we can throw the error message
|
|
return sq_throwerror(vm, "Unable to retrieve the query");
|
|
}
|
|
// Is there even a query to queue?
|
|
else if (IsQueryEmpty(sql))
|
|
{
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
return sq_throwerror(vm,"No query to queue");
|
|
}
|
|
// Attempt to queue the specified query
|
|
inst.value->m_Handle->mQueue.push_back(sql);
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
}
|
|
// Do we have enough values to call the format function?
|
|
else if (top > 2)
|
|
{
|
|
SStr sql = NULL;
|
|
SQInteger len = 0;
|
|
// Attempt to generate the specified string format
|
|
SQRESULT ret = sqstd_format(vm, 3, &len, &sql);
|
|
// Did the format failed?
|
|
if (SQ_FAILED(ret))
|
|
return ret;
|
|
// Is there even a query to queue?
|
|
else if (IsQueryEmpty(sql))
|
|
return sq_throwerror(vm,"No query to queue");
|
|
// Attempt to queue the specified query
|
|
inst.value->m_Handle->mQueue.push_back(sql);
|
|
}
|
|
// All methods of retrieving the message value failed
|
|
else
|
|
return sq_throwerror(vm, "Unable to extract the query string");
|
|
// This function does not return a value
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Connection::QueryF(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Do we even have enough arguments?
|
|
if (top <= 1)
|
|
return sq_throwerror(vm, "Missing the query string");
|
|
// Obtain the connection instance
|
|
Var< Connection * > inst(vm, 1);
|
|
// Do we have a valid connection instance?
|
|
if (!inst.value)
|
|
return sq_throwerror(vm, "Invalid connection instance");
|
|
// Do we have a valid connection reference?
|
|
else if (!inst.value->m_Handle)
|
|
return sq_throwerror(vm, "Invalid SQLite connection reference");
|
|
// Is the specified message value a string or something convertible to string?
|
|
else if (top == 2 && ((sq_gettype(vm, -1) == OT_STRING) || !SQ_FAILED(sq_tostring(vm, -1))))
|
|
{
|
|
CCStr sql = NULL;
|
|
// Attempt to retrieve the string from the stack
|
|
if (SQ_FAILED(sq_getstring(vm, -1, &sql)))
|
|
{
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// Now we can throw the error message
|
|
return sq_throwerror(vm, "Unable to retrieve the query");
|
|
}
|
|
// Attempt to create a statement with the specified query
|
|
ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql));
|
|
// If the value was converted to a string then pop the string
|
|
sq_pop(vm, sq_gettop(vm) - top);
|
|
// See if any errors occured
|
|
if (Sqrat::Error::Occurred(vm))
|
|
{
|
|
// Obtain the error message from sqrat
|
|
String msg = Sqrat::Error::Message(vm);
|
|
// Throw the error message further down the line
|
|
return sq_throwerror(vm, msg.c_str());
|
|
}
|
|
}
|
|
// Do we have enough values to call the format function?
|
|
else if (top > 2)
|
|
{
|
|
SStr sql = NULL;
|
|
SQInteger len = 0;
|
|
// Attempt to generate the specified string format
|
|
SQRESULT ret = sqstd_format(vm, 3, &len, &sql);
|
|
// Did the format failed?
|
|
if (SQ_FAILED(ret))
|
|
return ret;
|
|
// Attempt to create a statement with the specified query
|
|
ClassType< Statement >::PushInstance(vm, new Statement(inst.value->m_Handle, sql));
|
|
// See if any errors occured
|
|
if (Sqrat::Error::Occurred(vm))
|
|
{
|
|
// Obtain the error message from sqrat
|
|
String msg = Sqrat::Error::Message(vm);
|
|
// Throw the error message further down the line
|
|
return sq_throwerror(vm, msg.c_str());
|
|
}
|
|
}
|
|
// All methods of retrieving the message value failed
|
|
else
|
|
return sq_throwerror(vm, "Unable to extract the query string");
|
|
// At this point we should have a return value on the stack
|
|
return 1;
|
|
}
|
|
|
|
} // Namespace:: SqMod
|