#include <cassert>
#include <cstdio>
#include <cstring>
#include <memory>
#include <cstdlib>
#include "sqdb.hpp"
#include <unistd.h>
using namespace sqdb;
Exception::Exception(sqlite3* db)
{
m_errorCode = sqlite3_errcode(db);
auto* c = (SQDB_CHAR*)
#ifdef SQDB_UTF8
sqlite3_errmsg
#else
sqlite3_errmsg16
#endif
(db);
m_errorMsg = SQDB_STRDUP(c);
}
Exception::Exception(sqlite3* db, int errorCode)
: m_errorCode(errorCode)
{
auto* c = (SQDB_CHAR*)
#ifdef SQDB_UTF8
sqlite3_errmsg
#else
sqlite3_errmsg16
#endif
(db);
m_errorMsg = SQDB_STRDUP(c);
}
Exception::Exception(const SQDB_CHAR* errorMsg)
: m_errorCode(-1)
{
m_errorMsg = SQDB_STRDUP(errorMsg);
}
Exception::~Exception()
{
free(m_errorMsg);
}
int Exception::GetErrorCode() const
{
return m_errorCode;
}
const SQDB_CHAR* Exception::GetErrorMsg() const
{
return m_errorMsg;
}
RefCount::RefCount()
: m_refCount(nullptr)
{
}
RefCount::RefCount(const RefCount& x)
: m_refCount(x.m_refCount)
{
}
RefCount& RefCount::operator=(const RefCount& x)
{
if ( this != &x )
{
m_refCount = x.m_refCount;
}
return *this;
}
void RefCount::IncRef()
{
if ( !m_refCount )
{
m_refCount = new unsigned;
*m_refCount = 0;
}
++*m_refCount;
}
unsigned RefCount::DecRef()
{
assert(m_refCount);
unsigned value = --*m_refCount;
if ( value == 0 )
{
delete m_refCount;
m_refCount = nullptr;
}
return value;
}
Blob::Blob(const void* data, int size)
: m_size(size)
{
m_data = new char[size];
std::uninitialized_copy((char*)data, (char*)data + size, m_data);
IncRef();
}
Blob::Blob(const Blob& x)
: RefCount(x), m_data(x.m_data), m_size(x.m_size)
{
IncRef();
}
Blob& Blob::operator=(const Blob& x)
{
if ( this != &x )
{
RefCount::operator=(x);
IncRef();
m_data = x.m_data;
m_size = x.m_size;
}
return *this;
}
int Blob::GetSize() const
{
return m_size;
}
const char* Blob::GetData() const
{
return m_data;
}
Blob::~Blob()
{
if ( DecRef() == 0 )
{
delete[] m_data;
}
}
Convertor::Convertor(sqlite3* db, sqlite3_stmt* stmt, int field)
: m_db(db), m_stmt(stmt), m_field(field)
{
}
Convertor::operator int() const
{
return GetInt();
}
Convertor::operator unsigned long() const
{
return GetUnsignedLong();
}
Convertor::operator long long() const
{
return GetLongLong();
}
Convertor::operator double() const
{
return GetDouble();
}
Convertor::operator SQDB_STD_STRING() const
{
return GetString();
}
Convertor::operator const SQDB_CHAR*() const
{
return GetText();
}
Convertor::operator Blob() const
{
return GetBlob();
}
int Convertor::GetInt() const
{
assert(m_stmt);
return sqlite3_column_int(m_stmt, m_field);
}
unsigned long Convertor::GetUnsignedLong() const
{
assert(m_stmt);
const char* data = (char*) sqlite3_column_blob(m_stmt, m_field);
return strtoul(data, nullptr, 10);
}
long long Convertor::GetLongLong() const
{
assert(m_stmt);
return sqlite3_column_int64(m_stmt, m_field);
}
double Convertor::GetDouble() const
{
assert(m_stmt);
return sqlite3_column_double(m_stmt, m_field);
}
SQDB_STD_STRING Convertor::GetString() const
{
assert(m_stmt);
const auto* result = (const SQDB_CHAR*)
#ifdef SQDB_UTF8
sqlite3_column_text
#else
sqlite3_column_text16
#endif
(m_stmt, m_field);
return result;
}
const SQDB_CHAR* Convertor::GetText() const
{
assert(m_stmt);
const auto* result = (const SQDB_CHAR*)
#ifdef SQDB_UTF8
sqlite3_column_text
#else
sqlite3_column_text16
#endif
(m_stmt, m_field);
return result;
}
Blob Convertor::GetBlob() const
{
assert(m_stmt);
const void* data = sqlite3_column_blob(m_stmt, m_field);
int size = sqlite3_column_bytes(m_stmt, m_field);
return Blob(data, size);
}
Statement::Statement(sqlite3* db, sqlite3_stmt* stmt)
: RefCount(), m_db(db), m_stmt(stmt), m_needReset(false)
{
IncRef();
}
Statement::Statement(const Statement& x)
: RefCount(x), m_db(x.m_db), m_stmt(x.m_stmt), m_needReset(false)
{
IncRef();
}
Statement& Statement::operator=(const Statement& x)
{
if ( this != &x )
{
RefCount::operator=(x);
IncRef();
m_db = x.m_db;
m_stmt = x.m_stmt;
m_needReset = x.m_needReset;
}
return *this;
}
bool Statement::Next() {
assert(m_stmt);
int ret = SQLITE_BUSY;
while (ret == SQLITE_BUSY) {
ret = sqlite3_step(m_stmt);
m_needReset = true;
if ( ret == SQLITE_DONE ) {
return false;
} else if ( ret == SQLITE_ROW ) {
return true;
} else if ( ret == SQLITE_BUSY ) {
sleep(1);
continue;
} else {
throw Exception(m_db, ret);
}
}
return false;
}
Convertor Statement::GetField(int field) const
{
return {m_db, m_stmt, field};
}
Statement::~Statement()
{
if ( DecRef() == 0 )
{
sqlite3_finalize(m_stmt);
}
}
void Statement::BindBlob(int i, const void* value, int n)
{
if ( m_needReset )
Reset();
DoBind(i, value, n);
}
void Statement::BindNull(int i)
{
if ( m_needReset )
Reset();
DoBind(i);
}
void Statement::DoBind(int i, int value)
{
const int ret = sqlite3_bind_int(m_stmt, i, value);
CHECK(m_db, ret);
}
void Statement::DoBind(int i, long long value)
{
const int ret = sqlite3_bind_int64(m_stmt, i, value);
CHECK(m_db, ret);
}
void Statement::DoBind(int i, double value)
{
const int ret = sqlite3_bind_double(m_stmt, i, value);
CHECK(m_db, ret);
}
void Statement::DoBind(int i, const SQDB_STD_STRING& value)
{
const int ret =
#ifdef SQDB_UTF8
sqlite3_bind_text
#else
sqlite3_bind_text16
#endif
(m_stmt, i, value.c_str(), value.size() * sizeof(SQDB_STD_STRING::value_type), SQLITE_TRANSIENT);
CHECK(m_db, ret);
}
void Statement::DoBind(int i, const SQDB_CHAR* value)
{
const int len = SQDB_STRLEN(value);
const int ret =
#ifdef SQDB_UTF8
sqlite3_bind_text
#else
sqlite3_bind_text16
#endif
(m_stmt, i, value, len * sizeof(SQDB_CHAR), SQLITE_TRANSIENT);
CHECK(m_db, ret);
}
void Statement::DoBind(int i, const void* value, int n)
{
const int ret = sqlite3_bind_blob(m_stmt, i, value, n, SQLITE_TRANSIENT);
CHECK(m_db, ret);
}
void Statement::DoBind(int i)
{
const int ret = sqlite3_bind_null(m_stmt, i);
CHECK(m_db, ret);
}
void Statement::Reset()
{
assert(m_needReset);
assert(m_stmt);
sqlite3_reset(m_stmt);
m_needReset = false;
}
QueryStr::QueryStr()
: m_buf(nullptr)
{
}
const SQDB_CHAR* QueryStr::Format(const SQDB_CHAR* fmt, ...)
{
va_list va;
va_start(va, fmt);
#ifdef SQDB_UTF8
sqlite3_free(m_buf);
m_buf = sqlite3_vmprintf(fmt, va);
#else
free(m_buf);
int len = _vscwprintf(fmt, va) + 1;
m_buf = (SQDB_CHAR*)malloc(len * sizeof(SQDB_CHAR));
vswprintf(m_buf, len, fmt, va);
#endif
va_end(va);
return m_buf;
}
const SQDB_CHAR* QueryStr::Get() const
{
return m_buf;
}
QueryStr::~QueryStr()
{
#ifdef SQDB_UTF8
sqlite3_free(m_buf);
#else
free(m_buf);
#endif
}
Db::Db(const SQDB_CHAR* fileName)
: RefCount()
{
#ifdef SQDB_UTF8
const int ret = sqlite3_open(fileName, &m_db);
#else
const int ret = sqlite3_open16(fileName, &m_db);
#endif
CHECK(m_db, ret);
IncRef();
}
void Db::BeginTransaction()
{
Query(SQDB_MAKE_TEXT("BEGIN;")).Next();
}
void Db::CommitTransaction()
{
Query(SQDB_MAKE_TEXT("COMMIT;")).Next();
}
void Db::RollbackTransaction()
{
Query(SQDB_MAKE_TEXT("ROLLBACK;")).Next();
}
bool Db::TableExists(const SQDB_CHAR* tableName)
{
QueryStr str;
Statement s =
Query(
str.Format(SQDB_MAKE_TEXT("select count(*) from sqlite_master where type='table' and name='%s';"), tableName));
s.Next();
const int count = s.GetField(0);
return count > 0;
}
Statement Db::Query(const SQDB_CHAR* queryStr)
{
sqlite3_stmt* stmt = nullptr;
#ifdef SQDB_UTF8
const int ret = sqlite3_prepare(m_db, queryStr, -1, &stmt, nullptr);
#else
const int ret = sqlite3_prepare16(m_db, queryStr, -1, &stmt, NULL);
#endif
CHECK(m_db, ret);
return Statement(m_db, stmt);
}
long long Db::LastId()
{
long long ret = sqlite3_last_insert_rowid(m_db);
return ret;
}
Db::Db(const Db& x)
: RefCount(x),
m_db(x.m_db)
{
IncRef();
}
Db& Db::operator=(const Db& x)
{
if ( this != &x )
{
RefCount::operator=(x);
IncRef();
m_db = x.m_db;
}
return *this;
}
Db::~Db()
{
if ( DecRef() == 0 )
{
sqlite3_close(m_db);
}
}