aboutsummaryrefslogtreecommitdiffstats
path: root/src/sqdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sqdb.cpp')
-rw-r--r--src/sqdb.cpp437
1 files changed, 437 insertions, 0 deletions
diff --git a/src/sqdb.cpp b/src/sqdb.cpp
new file mode 100644
index 0000000..4a04f05
--- /dev/null
+++ b/src/sqdb.cpp
@@ -0,0 +1,437 @@
+#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);
+ }
+}
+