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

Implemented the SQLite transaction class using the RAII pattern.

Fixed an issue that generated bad messages for errors that occurred in the formatted query execution method.
Minor adjustments throughout the code structure.
This commit is contained in:
Sandu Liviu Catalin 2016-03-23 05:43:19 +02:00
parent 133bedce50
commit 1a312f7e7f
4 changed files with 217 additions and 16 deletions

View File

@ -1,6 +1,7 @@
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
#include "Common.hpp" #include "Common.hpp"
#include "Module.hpp" #include "Module.hpp"
#include "Connection.hpp"
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
#include <ctype.h> #include <ctype.h>
@ -322,6 +323,70 @@ Int32 StmtHnd::Handle::GetColumnIndex(CSStr name)
return -1; return -1;
} }
// ------------------------------------------------------------------------------------------------
Transaction::Transaction(const Connection & db)
: Transaction(db.GetHandle())
{
/* ... */
}
// ------------------------------------------------------------------------------------------------
Transaction::Transaction(const ConnHnd & db)
: m_Connection(db), m_Committed(false)
{
// Was the specified database connection valid?
if (!m_Connection)
{
STHROWF("Invalid connection handle");
}
// Attempt to begin transaction
else if ((m_Connection = sqlite3_exec(m_Connection, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK)
{
STHROWF("Unable to begin transaction [%s]", m_Connection.ErrMsg());
}
}
// ------------------------------------------------------------------------------------------------
Transaction::~Transaction()
{
// Was this transaction successfully committed?
if (m_Committed)
{
return; // We're done here!
}
// Attempt to roll back changes because this failed to commit
if ((m_Connection = sqlite3_exec(m_Connection, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK)
{
STHROWF("Unable to rollback transaction [%s]", m_Connection.ErrMsg());
}
}
// ------------------------------------------------------------------------------------------------
bool Transaction::Commit()
{
// We shouldn't even be here if there wasn't a valid connection but let's be sure
if (!m_Connection)
{
STHROWF("Invalid database connection");
}
// Was this transaction already committed?
else if (m_Committed)
{
STHROWF("Transaction was already committed");
}
// Attempt to commit the change during this transaction
else if ((m_Connection = sqlite3_exec(m_Connection, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK)
{
STHROWF("Unable to commit transaction [%s]", m_Connection.ErrMsg());
}
else
{
m_Committed = true; // Everything was committed successfully
}
// Return the result
return m_Committed;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CSStr GetErrStr(Int32 status) CSStr GetErrStr(Int32 status)
{ {

View File

@ -212,8 +212,10 @@ private:
void Grab() void Grab()
{ {
if (m_Hnd) if (m_Hnd)
{
++(m_Hnd->mRef); ++(m_Hnd->mRef);
} }
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Drop a strong reference to a connection handle. * Drop a strong reference to a connection handle.
@ -221,8 +223,10 @@ private:
void Drop() void Drop()
{ {
if (m_Hnd && --(m_Hnd->mRef) == 0) if (m_Hnd && --(m_Hnd->mRef) == 0)
{
delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) delete m_Hnd; // Let the destructor take care of cleaning up (if necessary)
} }
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Base constructor. * Base constructor.
@ -304,7 +308,9 @@ public:
ConnHnd & operator = (Int32 status) ConnHnd & operator = (Int32 status)
{ {
if (m_Hnd) if (m_Hnd)
{
m_Hnd->mStatus = status; m_Hnd->mStatus = status;
}
return *this; return *this;
} }
@ -330,7 +336,9 @@ public:
bool operator == (Int32 status) const bool operator == (Int32 status) const
{ {
if (m_Hnd) if (m_Hnd)
{
return (m_Hnd->mStatus == status); return (m_Hnd->mStatus == status);
}
return false; return false;
} }
@ -340,7 +348,9 @@ public:
bool operator != (Int32 status) const bool operator != (Int32 status) const
{ {
if (m_Hnd) if (m_Hnd)
{
return (m_Hnd->mStatus != status); return (m_Hnd->mStatus != status);
}
return false; return false;
} }
@ -428,6 +438,14 @@ public:
return m_Hnd ? m_Hnd->mRef : 0; return m_Hnd ? m_Hnd->mRef : 0;
} }
/* --------------------------------------------------------------------------------------------
* Retrieve the last known status code.
*/
Int32 Status() const
{
return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM;
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Retrieve the message of the last received error code. * Retrieve the message of the last received error code.
*/ */
@ -435,7 +453,9 @@ public:
{ {
// SQLite does it's null pointer validations internally // SQLite does it's null pointer validations internally
if (m_Hnd) if (m_Hnd)
{
return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr)); return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr));
}
return _SC(""); return _SC("");
} }
@ -446,7 +466,9 @@ public:
{ {
// SQLite does it's null pointer validations internally // SQLite does it's null pointer validations internally
if (m_Hnd) if (m_Hnd)
{
return sqlite3_errmsg(m_Hnd->mPtr); return sqlite3_errmsg(m_Hnd->mPtr);
}
return _SC(""); return _SC("");
} }
@ -457,7 +479,9 @@ public:
{ {
// SQLite does it's null pointer validations internally // SQLite does it's null pointer validations internally
if (m_Hnd) if (m_Hnd)
{
return sqlite3_errcode(m_Hnd->mPtr); return sqlite3_errcode(m_Hnd->mPtr);
}
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
@ -468,7 +492,9 @@ public:
{ {
// SQLite does it's null pointer validations internally // SQLite does it's null pointer validations internally
if (m_Hnd) if (m_Hnd)
{
return sqlite3_extended_errcode(m_Hnd->mPtr); return sqlite3_extended_errcode(m_Hnd->mPtr);
}
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
}; };
@ -581,8 +607,10 @@ private:
void Grab() void Grab()
{ {
if (m_Hnd) if (m_Hnd)
{
++(m_Hnd->mRef); ++(m_Hnd->mRef);
} }
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Drop a strong reference to a statement handle. * Drop a strong reference to a statement handle.
@ -590,8 +618,10 @@ private:
void Drop() void Drop()
{ {
if (m_Hnd && --(m_Hnd->mRef) == 0) if (m_Hnd && --(m_Hnd->mRef) == 0)
{
delete m_Hnd; // Let the destructor take care of cleaning up (if necessary) delete m_Hnd; // Let the destructor take care of cleaning up (if necessary)
} }
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Base constructor. * Base constructor.
@ -674,7 +704,9 @@ public:
StmtHnd & operator = (Int32 status) StmtHnd & operator = (Int32 status)
{ {
if (m_Hnd) if (m_Hnd)
{
m_Hnd->mStatus = status; m_Hnd->mStatus = status;
}
return *this; return *this;
} }
@ -700,7 +732,9 @@ public:
bool operator == (Int32 status) const bool operator == (Int32 status) const
{ {
if (m_Hnd) if (m_Hnd)
{
return (m_Hnd->mStatus == status); return (m_Hnd->mStatus == status);
}
return false; return false;
} }
@ -710,7 +744,9 @@ public:
bool operator != (Int32 status) const bool operator != (Int32 status) const
{ {
if (m_Hnd) if (m_Hnd)
{
return (m_Hnd->mStatus != status); return (m_Hnd->mStatus != status);
}
return false; return false;
} }
@ -798,13 +834,23 @@ public:
return m_Hnd ? m_Hnd->mRef : 0; return m_Hnd ? m_Hnd->mRef : 0;
} }
/* --------------------------------------------------------------------------------------------
* Retrieve the last known status code.
*/
Int32 Status() const
{
return m_Hnd ? m_Hnd->mStatus : SQLITE_NOMEM;
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Retrieve the message of the last received error code. * Retrieve the message of the last received error code.
*/ */
CCStr ErrStr() const CCStr ErrStr() const
{ {
if (m_Hnd) if (m_Hnd)
{
return m_Hnd->mConn.ErrStr(); return m_Hnd->mConn.ErrStr();
}
return _SC(""); return _SC("");
} }
@ -814,7 +860,9 @@ public:
CCStr ErrMsg() const CCStr ErrMsg() const
{ {
if (m_Hnd) if (m_Hnd)
{
return m_Hnd->mConn.ErrMsg(); return m_Hnd->mConn.ErrMsg();
}
return _SC(""); return _SC("");
} }
@ -824,7 +872,9 @@ public:
Int32 ErrNo() const Int32 ErrNo() const
{ {
if (m_Hnd) if (m_Hnd)
{
return m_Hnd->mConn.ErrNo(); return m_Hnd->mConn.ErrNo();
}
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
@ -834,11 +884,75 @@ public:
Int32 ExErrNo() const Int32 ExErrNo() const
{ {
if (m_Hnd) if (m_Hnd)
{
return m_Hnd->mConn.ExErrNo(); return m_Hnd->mConn.ExErrNo();
}
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
}; };
/* ------------------------------------------------------------------------------------------------
* Implements the RAII pattern for database transactions.
*/
class Transaction
{
public:
/* --------------------------------------------------------------------------------------------
* Construct by taking the handle from a connection.
*/
Transaction(const Connection & db);
/* --------------------------------------------------------------------------------------------
* Construct using the direct connection handle.
*/
Transaction(const ConnHnd & db);
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Transaction(const Transaction & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Transaction(Transaction && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Transaction();
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Transaction & operator = (const Transaction & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Transaction & operator = (Transaction && o) = delete;
/* --------------------------------------------------------------------------------------------
* Attempt to commit changes to the database.
*/
bool Commit();
/* --------------------------------------------------------------------------------------------
* See whether the change during this transaction were successfully committed.
*/
bool Commited() const
{
return m_Committed;
}
private:
// --------------------------------------------------------------------------------------------
ConnHnd m_Connection; // The database connection handle where the transaction began.
bool m_Committed; // Whether changes were successfully committed to the database.
};
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
* Retrieve the string representation of a certain status code. * Retrieve the string representation of a certain status code.
*/ */

View File

@ -386,7 +386,14 @@ SQInteger Connection::ExecF(HSQUIRRELVM vm)
// Attempt to execute the specified query // Attempt to execute the specified query
else if ((conn->m_Handle = sqlite3_exec(conn->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK) else if ((conn->m_Handle = sqlite3_exec(conn->m_Handle, sql, NULL, NULL, NULL)) != SQLITE_OK)
{ {
return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", conn->m_Handle.ErrMsg())); // Generate the query message first
String msg("Unable to execute query ");
// (we can't use FmtStr here because Squirrel doesn't make a copy of the message)
msg.push_back('[');
msg.append(conn->m_Handle.ErrMsg());
msg.push_back(']');
// Now throw the message
return sq_throwerror(vm, msg.c_str());
} }
} }
else else
@ -401,7 +408,14 @@ SQInteger Connection::ExecF(HSQUIRRELVM vm)
// Attempt to execute the specified query // Attempt to execute the specified query
else if ((conn->m_Handle = sqlite3_exec(conn->m_Handle, sql.value, NULL, NULL, NULL)) != SQLITE_OK) else if ((conn->m_Handle = sqlite3_exec(conn->m_Handle, sql.value, NULL, NULL, NULL)) != SQLITE_OK)
{ {
return sq_throwerror(vm, FmtStr("Unable to execute query [%s]", conn->m_Handle.ErrMsg())); // Generate the query message first
String msg("Unable to execute query ");
// (we can't use FmtStr here because Squirrel doesn't make a copy of the message)
msg.push_back('[');
msg.append(conn->m_Handle.ErrMsg());
msg.push_back(']');
// Now throw the message
return sq_throwerror(vm, msg.c_str());
} }
} }
// Push the number of changes onto the stack // Push the number of changes onto the stack

View File

@ -170,16 +170,16 @@ void RegisterAPI(HSQUIRRELVM vm)
Table sqlns(vm); Table sqlns(vm);
sqlns.Bind(_SC("Connection"), Class< Connection >(vm, _SC("SqSQLiteConnection")) sqlns.Bind(_SC("Connection"), Class< Connection >(vm, _SC("SqSQLiteConnection"))
/* Constructors */ // Constructors
.Ctor() .Ctor()
.Ctor< CCStr >() .Ctor< CCStr >()
.Ctor< CCStr, Int32 >() .Ctor< CCStr, Int32 >()
.Ctor< CCStr, Int32, CCStr >() .Ctor< CCStr, Int32, CCStr >()
/* Metamethods */ // Metamethods
.Func(_SC("_cmp"), &Connection::Cmp) .Func(_SC("_cmp"), &Connection::Cmp)
.SquirrelFunc(_SC("_typename"), &Connection::Typename) .SquirrelFunc(_SC("_typename"), &Connection::Typename)
.Func(_SC("_tostring"), &Connection::ToString) .Func(_SC("_tostring"), &Connection::ToString)
/* Properties */ // Properties
.Prop(_SC("Valid"), &Connection::IsValid) .Prop(_SC("Valid"), &Connection::IsValid)
.Prop(_SC("Refs"), &Connection::GetRefCount) .Prop(_SC("Refs"), &Connection::GetRefCount)
.Prop(_SC("Connected"), &Connection::IsValid) .Prop(_SC("Connected"), &Connection::IsValid)
@ -201,7 +201,7 @@ void RegisterAPI(HSQUIRRELVM vm)
.Prop(_SC("Profile"), &Connection::GetProfiling, &Connection::SetProfiling) .Prop(_SC("Profile"), &Connection::GetProfiling, &Connection::SetProfiling)
.Prop(_SC("BusyTimeout"), (Int32 (Connection::*)(void) const)(NULL), &Connection::SetBusyTimeout) .Prop(_SC("BusyTimeout"), (Int32 (Connection::*)(void) const)(NULL), &Connection::SetBusyTimeout)
.Prop(_SC("QueueSize"), &Connection::QueueSize) .Prop(_SC("QueueSize"), &Connection::QueueSize)
/* Functions */ // Member Methods
.Func(_SC("Release"), &Connection::Release) .Func(_SC("Release"), &Connection::Release)
.Overload< void (Connection::*)(CSStr) >(_SC("Open"), &Connection::Open) .Overload< void (Connection::*)(CSStr) >(_SC("Open"), &Connection::Open)
.Overload< void (Connection::*)(CSStr, Int32) >(_SC("Open"), &Connection::Open) .Overload< void (Connection::*)(CSStr, Int32) >(_SC("Open"), &Connection::Open)
@ -229,15 +229,15 @@ void RegisterAPI(HSQUIRRELVM vm)
); );
sqlns.Bind(_SC("Statement"), Class< Statement >(vm, _SC("SqSQLiteStatement")) sqlns.Bind(_SC("Statement"), Class< Statement >(vm, _SC("SqSQLiteStatement"))
/* Constructors */ // Constructors
.Ctor() .Ctor()
.Ctor< const Connection &, CCStr >() .Ctor< const Connection &, CCStr >()
.Ctor< const Statement & >() .Ctor< const Statement & >()
/* Metamethods */ // Metamethods
.Func(_SC("_cmp"), &Statement::Cmp) .Func(_SC("_cmp"), &Statement::Cmp)
.SquirrelFunc(_SC("_typename"), &Statement::Typename) .SquirrelFunc(_SC("_typename"), &Statement::Typename)
.Func(_SC("_tostring"), &Statement::ToString) .Func(_SC("_tostring"), &Statement::ToString)
/* Properties */ // Properties
.Prop(_SC("Valid"), &Statement::IsValid) .Prop(_SC("Valid"), &Statement::IsValid)
.Prop(_SC("Refs"), &Statement::GetRefCount) .Prop(_SC("Refs"), &Statement::GetRefCount)
.Prop(_SC("Conn"), &Statement::GetConnection) .Prop(_SC("Conn"), &Statement::GetConnection)
@ -252,7 +252,7 @@ void RegisterAPI(HSQUIRRELVM vm)
.Prop(_SC("Query"), &Statement::GetQuery) .Prop(_SC("Query"), &Statement::GetQuery)
.Prop(_SC("Good"), &Statement::GetGood) .Prop(_SC("Good"), &Statement::GetGood)
.Prop(_SC("Done"), &Statement::GetDone) .Prop(_SC("Done"), &Statement::GetDone)
/* Functions */ // Member Methods
.Func(_SC("Release"), &Statement::Release) .Func(_SC("Release"), &Statement::Release)
.Func(_SC("Reset"), &Statement::Reset) .Func(_SC("Reset"), &Statement::Reset)
.Func(_SC("Clear"), &Statement::Clear) .Func(_SC("Clear"), &Statement::Clear)
@ -299,14 +299,14 @@ void RegisterAPI(HSQUIRRELVM vm)
); );
sqlns.Bind(_SC("Column"), Class< Column >(vm, _SC("SqSQLiteColumn")) sqlns.Bind(_SC("Column"), Class< Column >(vm, _SC("SqSQLiteColumn"))
/* Constructors */ // Constructors
.Ctor() .Ctor()
.Ctor< const Column & >() .Ctor< const Column & >()
/* Metamethods */ // Metamethods
.Func(_SC("_cmp"), &Column::Cmp) .Func(_SC("_cmp"), &Column::Cmp)
.SquirrelFunc(_SC("_typename"), &Column::Typename) .SquirrelFunc(_SC("_typename"), &Column::Typename)
.Func(_SC("_tostring"), &Column::ToString) .Func(_SC("_tostring"), &Column::ToString)
/* Properties */ // Properties
.Prop(_SC("Valid"), &Column::IsValid) .Prop(_SC("Valid"), &Column::IsValid)
.Prop(_SC("Refs"), &Column::GetRefCount) .Prop(_SC("Refs"), &Column::GetRefCount)
.Prop(_SC("Index"), &Column::GetIndex) .Prop(_SC("Index"), &Column::GetIndex)
@ -325,10 +325,19 @@ void RegisterAPI(HSQUIRRELVM vm)
.Prop(_SC("OriginName"), &Column::GetOriginName) .Prop(_SC("OriginName"), &Column::GetOriginName)
.Prop(_SC("Type"), &Column::GetType) .Prop(_SC("Type"), &Column::GetType)
.Prop(_SC("Bytes"), &Column::GetBytes) .Prop(_SC("Bytes"), &Column::GetBytes)
/* Functions */ // Member Methods
.Func(_SC("Release"), &Column::Release) .Func(_SC("Release"), &Column::Release)
); );
sqlns.Bind(_SC("Transaction"), Class< Transaction, NoCopy< Transaction > >(vm, _SC("SqSQLiteTransaction"))
// Constructors
.Ctor< const Connection & >()
// Properties
.Prop(_SC("Committed"), &Transaction::Commited)
// Member Methods
.Func(_SC("Commit"), &Transaction::Commit)
);
sqlns.Func(_SC("IsQueryEmpty"), &IsQueryEmpty); sqlns.Func(_SC("IsQueryEmpty"), &IsQueryEmpty);
sqlns.Func(_SC("GetErrStr"), &GetErrStr); sqlns.Func(_SC("GetErrStr"), &GetErrStr);
sqlns.Func(_SC("SetSoftHeapLimit"), &SetSoftHeapLimit); sqlns.Func(_SC("SetSoftHeapLimit"), &SetSoftHeapLimit);
@ -343,7 +352,7 @@ void RegisterAPI(HSQUIRRELVM vm)
RootTable(vm).Bind(_SC("SQLite"), sqlns); RootTable(vm).Bind(_SC("SQLite"), sqlns);
/*
ConstTable(vm).Enum(_SC("ESQLite"), Enumeration(vm) ConstTable(vm).Enum(_SC("ESQLite"), Enumeration(vm)
.Const(_SC("ABORT"), SQLITE_ABORT) .Const(_SC("ABORT"), SQLITE_ABORT)
.Const(_SC("ABORT_ROLLBACK"), SQLITE_ABORT_ROLLBACK) .Const(_SC("ABORT_ROLLBACK"), SQLITE_ABORT_ROLLBACK)
@ -682,7 +691,6 @@ void RegisterAPI(HSQUIRRELVM vm)
.Const(_SC("WARNING"), SQLITE_WARNING) .Const(_SC("WARNING"), SQLITE_WARNING)
.Const(_SC("WARNING_AUTOINDEX"), SQLITE_WARNING_AUTOINDEX) .Const(_SC("WARNING_AUTOINDEX"), SQLITE_WARNING_AUTOINDEX)
); );
*/
} }
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------