1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 16:57:16 +01:00
SqMod/modules/sqlite/Handle/Connection.cpp
Sandu Liviu Catalin 4d77386b21 Update the SQLite module to work with the modified API.
Separate the SQLite handles into their own source files.
2016-06-03 21:31:00 +03:00

170 lines
5.9 KiB
C++

// ------------------------------------------------------------------------------------------------
#include "Handle/Connection.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
void ConnHnd::Validate() const
{
// Is the handle valid?
if ((m_Hnd == nullptr) || (m_Hnd->mPtr == nullptr))
{
STHROWF("Invalid SQLite connection reference");
}
}
// ------------------------------------------------------------------------------------------------
ConnHnd::Handle::~Handle()
{
// Is there anything to close?
if (!mPtr)
{
return; // Nothing to close
}
// Are we dealing with a memory leak? Technically shouldn't reach this situation!
else if (mRef != 0)
{
// Should we deal with undefined behavior instead? How bad is one connection left open?
_SqMod->LogErr("SQLite connection is still referenced (%s)", mName.c_str());
}
else
{
// NOTE: Should we call sqlite3_interrupt(...) before closing?
Object env;
Function func;
// Flush remaining queries in the queue and ignore the result
Flush(mQueue.size(), env, func);
// Attempt to close the database
if ((sqlite3_close(mPtr)) != SQLITE_OK)
{
_SqMod->LogErr("Unable to close SQLite connection [%s]", sqlite3_errmsg(mPtr));
}
}
}
// ------------------------------------------------------------------------------------------------
void ConnHnd::Handle::Create(CSStr name, Int32 flags, CSStr vfs)
{
// Make sure a previous connection doesn't exist
if (mPtr)
{
STHROWF("Unable to connect to database. Database already connected");
}
// Make sure the name is valid
else if (!name || *name == '\0')
{
STHROWF("Unable to connect to database. The name is invalid");
}
// Attempt to create the database connection
else if ((mStatus = sqlite3_open_v2(name, &mPtr, flags, vfs)) != SQLITE_OK)
{
// Grab the error message before destroying the handle
String msg(sqlite3_errmsg(mPtr) ? sqlite3_errmsg(mPtr) : _SC("Unknown reason"));
// Must be destroyed regardless of result
sqlite3_close(mPtr);
// Explicitly make sure it's null
mPtr = nullptr;
// Now its safe to throw the error
STHROWF("Unable to connect to database [%s]", msg.c_str());
}
// Let's save the specified information
mName.assign(name);
mFlags = flags;
mVFS.assign(vfs ? vfs : _SC(""));
// Optional check if database is initially stored in memory
mMemory = (mName.compare(_SC(":memory:")) == 0);
}
// ------------------------------------------------------------------------------------------------
Int32 ConnHnd::Handle::Flush(Uint32 num, Object & env, Function & func)
{
// Do we even have a valid connection?
if (!mPtr)
{
return -1; // No connection!
}
// Is there anything to flush?
else if (!num || mQueue.empty())
{
return 0; // Nothing to process!
}
// Can we even flush that many?
else if (num > mQueue.size())
{
num = mQueue.size();
}
// Generate the function that should be called upon error
Function callback = Function(env.GetVM(), env.GetObject(), func.GetFunc());
// Obtain iterators to the range of queries that should be flushed
QueryList::iterator itr = mQueue.begin();
QueryList::iterator end = mQueue.begin() + num;
// Attempt to begin the flush transaction
if ((mStatus = sqlite3_exec(mPtr, "BEGIN", nullptr, nullptr, nullptr)) != SQLITE_OK)
{
STHROWF("Unable to begin flush transaction [%s]", sqlite3_errmsg(mPtr));
}
// Process all queries within range of selection
for (; itr != end; ++itr)
{
// Should we manually terminate this query?
/*
if (*(*itr).rbegin() != ';')
{
itr->push_back(';');
}
*/
// Attempt to execute the currently processed query string
if ((mStatus = sqlite3_exec(mPtr, itr->c_str(), nullptr, nullptr, nullptr)) == SQLITE_OK)
{
continue;
}
// Do we have to execute any callback to resolve our issue?
else if (!callback.IsNull())
{
try
{
// Ask the callback whether the query processing should end here
SharedPtr< bool > ret = callback.Evaluate< bool, Int32, const String & >(mStatus, *itr);
// Should we break here?
if (!!ret && (*ret == false))
{
break;
}
}
catch (const Sqrat::Exception & e)
{
_SqMod->LogErr("Squirrel error caught in flush handler [%s]", e.Message().c_str());
}
catch (const std::exception & e)
{
_SqMod->LogErr("Program error caught in flush handler [%s]", e.what());
}
catch (...)
{
_SqMod->LogErr("Unknown error caught in flush handler");
}
}
}
// Erase all queries till end or till the point of failure (if any occurred)
mQueue.erase(mQueue.begin(), itr);
// Attempt to commit changes requested during transaction
if ((mStatus = sqlite3_exec(mPtr, "COMMIT", nullptr, nullptr, nullptr)) == SQLITE_OK)
{
return sqlite3_changes(mPtr);
}
// Attempt to roll back erroneous changes
else if ((mStatus = sqlite3_exec(mPtr, "ROLLBACK", nullptr, nullptr, nullptr)) != SQLITE_OK)
{
STHROWF("Unable to rollback flush transaction [%s]", sqlite3_errmsg(mPtr));
}
// The transaction failed somehow but we managed to rollback
else
{
STHROWF("Unable to commit flush transaction [%s]", sqlite3_errmsg(mPtr));
}
// Operation failed
return -1;
}
} // Namespace:: SqMod