#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);
}
}