mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 16:57:16 +01:00
832 lines
25 KiB
C++
832 lines
25 KiB
C++
#include "Library/SQLite/Connection.hpp"
|
|
#include "Library/SQLite/Statement.hpp"
|
|
|
|
namespace SqMod {
|
|
namespace SQLite {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection()
|
|
: m_Handle()
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(const SQChar * path)
|
|
: Connection(path, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(const SQChar * path, SQInt32 flags)
|
|
: Connection(path, flags, NULL)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(const SQChar * path, SQInt32 flags, const SQChar * vfs)
|
|
: m_Handle(path, flags, vfs)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection::Connection(const Handle & hnd)
|
|
: m_Handle(hnd)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Connection::Cmp(const Connection & o) const
|
|
{
|
|
if (m_Handle == o.m_Handle)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (m_Handle && (m_Handle->Ptr > o.m_Handle->Ptr))
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::ToString() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Path.c_str();
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetGlobalTag() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Tag.c_str();
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection tag> using an invalid reference");
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
void Connection::SetGlobalTag(const SQChar * tag) const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
m_Handle->Tag.assign(tag);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <set connection tag> using an invalid reference");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SqObj & Connection::GetGlobalData() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Data;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection data> using an invalid reference");
|
|
}
|
|
|
|
return NullData();
|
|
}
|
|
|
|
void Connection::SetGlobalData(SqObj & data) const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
m_Handle->Data = data;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <set connection data> using an invalid reference");
|
|
}
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetLocalTag() const
|
|
{
|
|
return m_Tag.c_str();
|
|
}
|
|
|
|
void Connection::SetLocalTag(const SQChar * tag)
|
|
{
|
|
m_Tag = tag;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SqObj & Connection::GetLocalData()
|
|
{
|
|
return m_Data;
|
|
}
|
|
|
|
void Connection::SetLocalData(SqObj & data)
|
|
{
|
|
m_Data = data;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetStatus() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Status;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection status> using an invalid connection: null");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetFlags() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Flags;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection flags> using an invalid connection: null");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetPath() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Path.c_str();
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection file path> using an invalid connection: null");
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetVFS() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->VFS.c_str();
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection vfs string> using an invalid connection: null");
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetErrStr() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle.ErrStr();
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection error string> using an invalid connection: null");
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * Connection::GetErrMsg() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle.ErrMsg();
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get connection error message> using an invalid connection: null");
|
|
}
|
|
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::Exec(const SQChar * str)
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
if ((m_Handle->Status = sqlite3_exec(m_Handle, static_cast< const char * >(str), NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <execute database query> because: %s", m_Handle.ErrMsg());
|
|
}
|
|
else
|
|
{
|
|
return sqlite3_changes(m_Handle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <execute database query> using an invalid connection: null");
|
|
}
|
|
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::Queue(const SQChar * str)
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
m_Handle->Queue.emplace(str);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <queue database query> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::TableExists(const SQChar * name) const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
// Create the statement which counts the tables with the specified name
|
|
Statement stmt(*this, "SELECT count(*) FROM [sqlite_master] WHERE [type]='table' AND [name]=?");
|
|
// See if the statement could be created
|
|
if (stmt.IsValid())
|
|
{
|
|
// Bind the table name
|
|
stmt.IndexBindS(1, name);
|
|
// Attempt to step the statement
|
|
if (stmt.Step())
|
|
{
|
|
LogErr("Unable to <see if table exists> because : the statement could not be stepped");
|
|
// Return the requested information
|
|
return (sqlite3_column_int(stmt, 0) == 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogErr("Unable to <see if table exists> because : the statement could not be created");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <see if table exists> using an invalid connection: null");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::IsReadOnly() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
const int result = sqlite3_db_readonly(m_Handle, "main");
|
|
// Verify the result
|
|
if(result == -1)
|
|
{
|
|
LogErr("Unable to <see if database is read only> because : 'main' is not the name of a database on connection");
|
|
}
|
|
// Return the result
|
|
else
|
|
{
|
|
return (result != 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <see if database is read only> using an invalid connection: null");
|
|
}
|
|
// It's invalid so at least fall-back to read-only
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::GetAutoCommit() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return sqlite3_get_autocommit(m_Handle);
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <see if database has autocommit activated> using an invalid connection: null");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SLongInt Connection::GetLastInsertRowID() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return SLongInt(static_cast< SLongInt::Type >(sqlite3_last_insert_rowid(m_Handle)));
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <get database changes> using an invalid connection: null");
|
|
}
|
|
|
|
return SLongInt(static_cast< SLongInt::Type >(SQMOD_UNKNOWN));
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetChanges() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return sqlite3_changes(m_Handle);
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <get database changes> using an invalid connection: null");
|
|
}
|
|
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetTotalChanges() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return sqlite3_total_changes(m_Handle);
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <get database total changes> using an invalid connection: null");
|
|
}
|
|
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetErrorCode() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return sqlite3_errcode(m_Handle);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get database error code> using an invalid connection: null");
|
|
}
|
|
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetExtendedErrorCode() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return sqlite3_extended_errcode(m_Handle);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get database extended error code> using an invalid connection: null");
|
|
}
|
|
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::GetTracing() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Trace;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <see if database has tracing activated> using an invalid connection: null");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::SetTracing(bool toggle) const
|
|
{
|
|
if (m_Handle && m_Handle->Trace != toggle)
|
|
{
|
|
if (m_Handle->Trace)
|
|
{
|
|
sqlite3_trace(m_Handle, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
sqlite3_trace(m_Handle, &Connection::TraceOutput, NULL);
|
|
}
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <activate database tracing> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Connection::GetProfiling() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
return m_Handle->Profile;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <see if database has profiling activated> using an invalid connection: null");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::SetProfiling(bool toggle) const
|
|
{
|
|
if (m_Handle && m_Handle->Profile != toggle)
|
|
{
|
|
if (m_Handle->Profile)
|
|
{
|
|
sqlite3_profile(m_Handle, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
sqlite3_profile(m_Handle, &Connection::ProfileOutput, NULL);
|
|
}
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <activate database profiling> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::SetBusyTimeout(SQInteger millis) const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
if ((m_Handle->Status = sqlite3_busy_timeout(m_Handle, millis)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <set database busy timeout> because : %s", m_Handle.ErrMsg());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <set database busy timeout> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::InterruptOperation() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
sqlite3_interrupt(m_Handle);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <interrupt database operation> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::ReleaseMemory() const
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
sqlite3_db_release_memory(m_Handle);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <release database memory> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetNumberOfCheckedOutLookasideMemorySlots() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_LOOKASIDE_USED);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetHeapMemoryUsedByPagerCaches() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_CACHE_USED);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetHeapMemoryUsedToStoreSchemas() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_SCHEMA_USED);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetHeapAndLookasideMemoryUsedByPreparedStatements() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_STMT_USED);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetPagerCacheHitCount() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_CACHE_HIT);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetPagerCacheMissCount() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_CACHE_MISS);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetNumberOfDirtyCacheEntries() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_CACHE_WRITE);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetNumberOfUnresolvedForeignKeys() const
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_DEFERRED_FKS);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetHighestNumberOfCheckedOutLookasideMemorySlots(bool reset)
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_LOOKASIDE_USED, true, reset);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetLookasideMemoryHitCount(bool reset)
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_LOOKASIDE_HIT, true, reset);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetLookasideMemoryMissCountDueToSmallSlotSize(bool reset)
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, true, reset);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetLookasideMemoryMissCountDueToFullMemory(bool reset)
|
|
{
|
|
return GetInfo(SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, true, reset);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection Connection::CopyToMemory()
|
|
{
|
|
if (m_Handle && !m_Handle->Memory)
|
|
{
|
|
// Attempt to open an in-memory database
|
|
Handle db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
|
// See if the database could be opened
|
|
if (!db)
|
|
{
|
|
LogErr("Unable to <open in-memory database> because : %s", db.ErrMsg());
|
|
}
|
|
// Begin a transaction to replicate the schema of origin database
|
|
else if ((m_Handle->Status = sqlite3_exec(m_Handle, "BEGIN", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <begin database schema replication> because : %s", m_Handle.ErrMsg());
|
|
}
|
|
// Attempt to replicate the schema of origin database to the in-memory one
|
|
else if ((m_Handle->Status = sqlite3_exec(m_Handle,
|
|
"SELECT [sql] FROM [sqlite_master] WHERE [sql] NOT NULL AND [tbl_name] != 'sqlite_sequence'",
|
|
&Connection::ProcessDDLRow, db, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <replicate database schema> because : %s", m_Handle.ErrMsg());
|
|
}
|
|
// Attempt to commit the changes to the database schema replication
|
|
else if ((m_Handle->Status = sqlite3_exec(m_Handle, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <commit database schema replication> because : %s", m_Handle.ErrMsg());
|
|
}
|
|
// Attempt to attach the origin database to the in-memory one
|
|
else if ((db->Status = sqlite3_exec(db, ToStringF("ATTACH DATABASE '%s' as origin",
|
|
m_Handle->Path.c_str()), NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <attach database origin> because : %s", db.ErrMsg());
|
|
}
|
|
// Begin a transaction to replicate the data of origin database
|
|
else if ((db->Status = sqlite3_exec(db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK))
|
|
{
|
|
LogErr("Unable to <begin database data replication> because : %s", db.ErrMsg());
|
|
}
|
|
// Attempt to replicate the data of origin database to the in-memory one
|
|
else if ((db->Status = sqlite3_exec(db, "SELECT [name] FROM [origin.sqlite_master] WHERE [type]='table'",
|
|
&Connection::ProcessDMLRow, db, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <replicate database data> because : %s", m_Handle.ErrMsg());
|
|
}
|
|
// Attempt to commit the changes to the database data replication
|
|
else if ((db->Status = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <commit database data replication> because : %s", db.ErrMsg());
|
|
// Attempt to rollback changes from the data copy operation
|
|
if ((db->Status = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <rollback database data replication> because : %s", db.ErrMsg());
|
|
}
|
|
// Attempt to detach the disk origin from in-memory database
|
|
else if ((db->Status = sqlite3_exec(db, "DETACH DATABASE origin", NULL, NULL, NULL)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <detach database origin> because : %s", db.ErrMsg());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Return the new in-memory database
|
|
return db;
|
|
}
|
|
}
|
|
else if (!m_Handle)
|
|
{
|
|
LogWrn("Attempting to <copy database to memory> using an invalid connection: null");
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <copy database to memory> for a database which is already in memory");
|
|
}
|
|
// Fall-back to an invalid connection
|
|
return Connection();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Connection Connection::CopyToDatabase(const SQChar * path)
|
|
{
|
|
if (m_Handle)
|
|
{
|
|
// See if the specified name is valid
|
|
if (strlen(path) <= 0)
|
|
{
|
|
LogWrn("Attempting to <replicate database> using an invalid path: null");
|
|
}
|
|
// Attempt to open the specified database
|
|
Handle db(path, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
|
// See if the database could be opened
|
|
if (!db)
|
|
{
|
|
LogErr("Unable to <open the specified database> because : %s", db.ErrMsg());
|
|
}
|
|
else
|
|
{
|
|
// Attempt to take snapshot of the database
|
|
TakeSnapshot(db);
|
|
// Return the database clone
|
|
return db;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <replicate database> using an invalid connection: null");
|
|
}
|
|
// Fall-back to an invalid connection
|
|
return Connection();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::TraceOutput(void * ptr, const char * sql)
|
|
{
|
|
SQMOD_UNUSED_VAR(ptr);
|
|
LogInf("SQLite Trace: %s", sql);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::ProfileOutput(void * ptr, const char * sql, sqlite3_uint64 time)
|
|
{
|
|
SQMOD_UNUSED_VAR(ptr);
|
|
LogInf("SQLite profile (time: %llu): %s", time, sql);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
int Connection::ProcessDDLRow(void * db, int columns_count, char ** values, char ** columns)
|
|
{
|
|
SQMOD_UNUSED_VAR(columns);
|
|
|
|
if(columns_count != 1)
|
|
{
|
|
LogErr("Error occurred during DDL: columns != 1");
|
|
// Operation failed
|
|
return -1;
|
|
}
|
|
// Execute the sql statement in values[0] in the received database connection
|
|
if(sqlite3_exec(static_cast< sqlite3 * >(db), values[0], NULL, NULL, NULL) != SQLITE_OK)
|
|
{
|
|
LogErr("Error occurred during DDL execution: %s", sqlite3_errmsg(static_cast< sqlite3 * >(db)));
|
|
}
|
|
// Operation succeeded
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
int Connection::ProcessDMLRow(void * db, int columns_count, char ** values, char ** columns)
|
|
{
|
|
SQMOD_UNUSED_VAR(columns);
|
|
|
|
if(columns_count != 1)
|
|
{
|
|
LogErr("Error occurred during DML: columns != 1");
|
|
// Operation failed
|
|
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 to the received database connection
|
|
if(sqlite3_exec(static_cast< sqlite3 * >(db), sql, NULL, NULL, NULL) != SQLITE_OK)
|
|
{
|
|
LogErr("Error occurred during DML execution: %s", sqlite3_errmsg(static_cast< sqlite3 * >(db)));
|
|
}
|
|
// Free the generated query string
|
|
sqlite3_free(sql);
|
|
// Operation succeeded
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Connection::TakeSnapshot(Handle & destination)
|
|
{
|
|
// Don't even bother to continue if there's no valid connection handle
|
|
if (m_Handle && 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)
|
|
{
|
|
// -1 to copy the entire source database to the destination
|
|
if((m_Handle->Status = sqlite3_backup_step(backup, -1)) != SQLITE_DONE)
|
|
{
|
|
LogErr("Unable to <copy database source> because: %s", sqlite3_errmsg(destination));
|
|
}
|
|
// clean up resources allocated by sqlite3_backup_init()
|
|
if((m_Handle->Status = sqlite3_backup_finish(backup)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <finalize database backup> because: %s", sqlite3_errmsg(destination));
|
|
}
|
|
}
|
|
}
|
|
else if (!destination)
|
|
{
|
|
LogWrn("Attempting to <take database snapshot> using an invalid destination: null");
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <take database snapshot> using an invalid connection: null");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 Connection::GetInfo(int operation, bool highwater, bool reset) const
|
|
{
|
|
// Don't even bother to continue if there's no valid connection handle
|
|
if (m_Handle)
|
|
{
|
|
int cur_value;
|
|
int hiwtr_value;
|
|
// Attempt to retrieve the specified information
|
|
if((m_Handle->Status = sqlite3_db_status(m_Handle, operation, &cur_value, &hiwtr_value, reset)) != SQLITE_OK)
|
|
{
|
|
LogErr("Unable to <get database runtime status information> because: %s", sqlite3_errmsg(m_Handle));
|
|
}
|
|
// Return what was requested
|
|
return highwater ? hiwtr_value : cur_value;
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <get database runtime status information> using an invalid connection: null");
|
|
}
|
|
return SQMOD_UNKNOWN;
|
|
}
|
|
|
|
} // Namespace:: SQLite
|
|
} // Namespace:: SqMod
|