From 9af086151e6d9d3fe88f9e3e21797812a3e701ba Mon Sep 17 00:00:00 2001 From: dec05eba <0xdec05eba@gmail.com> Date: Mon, 14 May 2018 00:20:11 +0200 Subject: Replace files with sqlite Using sqlite because sqlite has transactions, storing/loading from files automatically, unloading data that is not accessed often. Removed cosmetic data (node name, username). They can be added using addData by the application that uses odhtdb instead. Database callback functions can now be called with stored data using database.loadNode function. TODO: Add local user storage (with password) back, it has been temorary disabled --- src/sql/SqlQuery.cpp | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/sql/SqlQuery.cpp (limited to 'src/sql') diff --git a/src/sql/SqlQuery.cpp b/src/sql/SqlQuery.cpp new file mode 100644 index 0000000..47f1463 --- /dev/null +++ b/src/sql/SqlQuery.cpp @@ -0,0 +1,129 @@ +#include "../../include/odhtdb/sql/SqlQuery.hpp" +#include + +namespace odhtdb +{ + int SqlArg::bind(sqlite3_stmt *stmt, int paramIndex) const + { + switch(type) + { + case Type::DATA_VIEW: + return sqlite3_bind_blob(stmt, paramIndex, dataView.data, dataView.size, SQLITE_STATIC); + case Type::INT: + return sqlite3_bind_int(stmt, paramIndex, integer); + case Type::INT64: + return sqlite3_bind_int64(stmt, paramIndex, integer64); + } + return SQLITE_OK; + } + + SqlQuery::SqlQuery(sqlite3 *_db, const char *sql, std::initializer_list args) : + db(_db), + stmt(nullptr), + numColumns(0) + { + int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + if(rc != SQLITE_OK) + { + std::string errMsg = "Failed to prepare sqlite statement, error: "; + errMsg += sqlite3_errmsg(db); + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + throw SqlQueryException(errMsg); + } + + int numParams = sqlite3_bind_parameter_count(stmt); + if(args.size() != numParams) + { + std::string errMsg = "Failed to prepare sqlite statement, error: Sql has "; + errMsg += std::to_string(numParams); + errMsg += " parameters, got "; + errMsg += std::to_string(args.size()); + errMsg += " arguments"; + sqlite3_finalize(stmt); + stmt = nullptr; + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + throw SqlQueryException(errMsg); + } + + int paramIndex = 1; + for(const SqlArg &arg : args) + { + rc = arg.bind(stmt, paramIndex); + if(rc != SQLITE_OK) + { + std::string errMsg = "Failed to bind param, error code: "; + errMsg += std::to_string(rc); + sqlite3_finalize(stmt); + stmt = nullptr; + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + throw SqlQueryException(errMsg); + } + ++paramIndex; + } + } + + SqlQuery::~SqlQuery() + { + sqlite3_finalize(stmt); + } + + bool SqlQuery::next() + { + int rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) + return false; + else if(rc != SQLITE_ROW) + { + std::string errMsg = "Failed to perform sql select, error: "; + errMsg += sqlite3_errmsg(db); + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + throw SqlQueryException(errMsg); + } + + numColumns = sqlite3_data_count(stmt); + return true; + } + + void SqlQuery::checkColumnIndex(int index) + { + if(index < 0 || index >= numColumns) + { + std::string errMsg; + if(numColumns == 0) + { + errMsg += "Attempt to get column "; + errMsg += std::to_string(index); + errMsg += " but result does not have any columns"; + } + else + { + errMsg += "Column index "; + errMsg += std::to_string(index); + errMsg += " has to be between 0 and "; + errMsg += std::to_string(numColumns - 1); + } + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + throw SqlQueryException(errMsg); + } + } + + int SqlQuery::getInt(int index) + { + checkColumnIndex(index); + return sqlite3_column_int(stmt, index); + } + + i64 SqlQuery::getInt64(int index) + { + checkColumnIndex(index); + return sqlite3_column_int64(stmt, index); + } + + const DataView SqlQuery::getBlob(int index) + { + checkColumnIndex(index); + const void *data = sqlite3_column_blob(stmt, index); + int size = sqlite3_column_bytes(stmt, index); + return { (void*)data, (usize)size }; + } +} -- cgit v1.2.3