1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +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 "Module.hpp"
#include "Connection.hpp"
// ------------------------------------------------------------------------------------------------
#include <ctype.h>
@ -322,6 +323,70 @@ Int32 StmtHnd::Handle::GetColumnIndex(CSStr name)
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)
{

View File

@ -212,7 +212,9 @@ private:
void Grab()
{
if (m_Hnd)
{
++(m_Hnd->mRef);
}
}
/* --------------------------------------------------------------------------------------------
@ -221,7 +223,9 @@ private:
void Drop()
{
if (m_Hnd && --(m_Hnd->mRef) == 0)
{
delete m_Hnd; // Let the destructor take care of cleaning up (if necessary)
}
}
/* --------------------------------------------------------------------------------------------
@ -304,7 +308,9 @@ public:
ConnHnd & operator = (Int32 status)
{
if (m_Hnd)
{
m_Hnd->mStatus = status;
}
return *this;
}
@ -330,7 +336,9 @@ public:
bool operator == (Int32 status) const
{
if (m_Hnd)
{
return (m_Hnd->mStatus == status);
}
return false;
}
@ -340,7 +348,9 @@ public:
bool operator != (Int32 status) const
{
if (m_Hnd)
{
return (m_Hnd->mStatus != status);
}
return false;
}
@ -428,6 +438,14 @@ public:
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.
*/
@ -435,7 +453,9 @@ public:
{
// SQLite does it's null pointer validations internally
if (m_Hnd)
{
return sqlite3_errstr(sqlite3_errcode(m_Hnd->mPtr));
}
return _SC("");
}
@ -446,7 +466,9 @@ public:
{
// SQLite does it's null pointer validations internally
if (m_Hnd)
{
return sqlite3_errmsg(m_Hnd->mPtr);
}
return _SC("");
}
@ -457,7 +479,9 @@ public:
{
// SQLite does it's null pointer validations internally
if (m_Hnd)
{
return sqlite3_errcode(m_Hnd->mPtr);
}
return SQLITE_NOMEM;
}
@ -468,7 +492,9 @@ public:
{
// SQLite does it's null pointer validations internally
if (m_Hnd)
{
return sqlite3_extended_errcode(m_Hnd->mPtr);
}
return SQLITE_NOMEM;
}
};
@ -581,7 +607,9 @@ private:
void Grab()
{
if (m_Hnd)
{
++(m_Hnd->mRef);
}
}
/* --------------------------------------------------------------------------------------------
@ -590,7 +618,9 @@ private:
void Drop()
{
if (m_Hnd && --(m_Hnd->mRef) == 0)
{
delete m_Hnd; // Let the destructor take care of cleaning up (if necessary)
}
}
/* --------------------------------------------------------------------------------------------
@ -674,7 +704,9 @@ public:
StmtHnd & operator = (Int32 status)
{
if (m_Hnd)
{
m_Hnd->mStatus = status;
}
return *this;
}
@ -700,7 +732,9 @@ public:
bool operator == (Int32 status) const
{
if (m_Hnd)
{
return (m_Hnd->mStatus == status);
}
return false;
}
@ -710,7 +744,9 @@ public:
bool operator != (Int32 status) const
{
if (m_Hnd)
{
return (m_Hnd->mStatus != status);
}
return false;
}
@ -798,13 +834,23 @@ public:
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.
*/
CCStr ErrStr() const
{
if (m_Hnd)
{
return m_Hnd->mConn.ErrStr();
}
return _SC("");
}
@ -814,7 +860,9 @@ public:
CCStr ErrMsg() const
{
if (m_Hnd)
{
return m_Hnd->mConn.ErrMsg();
}
return _SC("");
}
@ -824,7 +872,9 @@ public:
Int32 ErrNo() const
{
if (m_Hnd)
{
return m_Hnd->mConn.ErrNo();
}
return SQLITE_NOMEM;
}
@ -834,11 +884,75 @@ public:
Int32 ExErrNo() const
{
if (m_Hnd)
{
return m_Hnd->mConn.ExErrNo();
}
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.
*/

View File

@ -386,7 +386,14 @@ SQInteger Connection::ExecF(HSQUIRRELVM vm)
// Attempt to execute the specified query
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
@ -401,7 +408,14 @@ SQInteger Connection::ExecF(HSQUIRRELVM vm)
// Attempt to execute the specified query
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

View File

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