#include "sqlite_connection.hpp" #include "sqlite_error.hpp" #include "sqlite_result_reader.hpp" #include "sqlite_statement.hpp" #include "matador/sql/record.hpp" #include #include #include namespace matador::backends::sqlite { sqlite_connection::sqlite_connection(const sql::connection_info &info) : connection_impl(info) { } void sqlite_connection::open() { if (is_open()) { return; } const auto ret = sqlite3_open(info().database.c_str(), &db_); if (ret != SQLITE_OK) { throw_sqlite_error(ret, db_, "open"); } } void sqlite_connection::close() { int ret = sqlite3_close(db_); throw_sqlite_error(ret, db_, "close"); db_ = nullptr; } bool sqlite_connection::is_open() { return db_ != nullptr; } int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns) { auto *context = static_cast(param); sqlite_result_reader::columns column; for(int i = 0; i < column_count; ++i) { // copy and store column data; if (values[i] == nullptr) { auto val = new char[1]; val[0] = '\0'; column.push_back(val); } else { size_t size = strlen(values[i]); auto val = new char[size + 1]; std::memcpy(val, values[i], size); val[size] = '\0'; column.push_back(val); } } context->rows.emplace_back(column); if (context->prototype.empty()) { for(int i = 0; i < column_count; ++i) { context->prototype.append(sql::column{columns[i]}); } } return 0; } sqlite_connection::fetch_context sqlite_connection::fetch_internal(const std::string &stmt) { fetch_context context; char *errmsg = nullptr; const int ret = sqlite3_exec(db_, stmt.c_str(), parse_result, &context, &errmsg); throw_sqlite_error(ret, db_, "sqlite", stmt); return context; } size_t sqlite_connection::execute(const std::string &stmt) { char *errmsg = nullptr; int ret = sqlite3_exec(db_, stmt.c_str(), nullptr, nullptr, &errmsg); throw_sqlite_error(ret, db_, "sqlite", stmt); return sqlite3_changes(db_); } std::unique_ptr sqlite_connection::fetch(const std::string &stmt) { auto context = fetch_internal(stmt); return std::move(std::make_unique(std::make_unique(std::move(context.rows), context.prototype.size()), std::move(context.prototype))); } std::unique_ptr sqlite_connection::prepare(sql::query_context query) { sqlite3_stmt *stmt{}; int ret = sqlite3_prepare_v2(db_, query.sql.c_str(), static_cast(query.sql.size()), &stmt, nullptr); throw_sqlite_error(ret, db_, "sqlite3_prepare_v2", query.sql); return std::make_unique(db_, stmt, query); } sql::data_type_t string2type(const char *type) { if (strncmp(type, "INTEGER", 7) == 0) { return sql::data_type_t::type_int; } else if (strncmp(type, "TINYINT", 7) == 0) { return sql::data_type_t::type_char; } else if (strncmp(type, "SMALLINT", 8) == 0) { return sql::data_type_t::type_short; } else if (strncmp(type, "BIGINT", 6) == 0) { return sql::data_type_t::type_long_long; } else if (strcmp(type, "BOOLEAN") == 0) { return sql::data_type_t::type_bool; } else if (strcmp(type, "REAL") == 0) { return sql::data_type_t::type_double; } else if (strcmp(type, "FLOAT") == 0) { return sql::data_type_t::type_float; } else if (strcmp(type, "DOUBLE") == 0) { return sql::data_type_t::type_double; } else if (strcmp(type, "BLOB") == 0) { return sql::data_type_t::type_blob; } else if (strcmp(type, "NULL") == 0) { return sql::data_type_t::type_null; } else if (strncmp(type, "VARCHAR", 7) == 0) { return sql::data_type_t::type_varchar; } else if (strcmp(type, "DATE") == 0) { return sql::data_type_t::type_date; } else if (strcmp(type, "DATETIME") == 0) { return sql::data_type_t::type_time; } else if (strcmp(type, "TEXT") == 0) { return sql::data_type_t::type_text; } else { return sql::data_type_t::type_unknown; } } sql::record sqlite_connection::describe(const std::string& table) { const auto result = fetch_internal("PRAGMA table_info(" + table + ")"); sqlite_result_reader reader(result.rows, result.prototype.size()); sql::record prototype; while (reader.fetch()) { char *end = nullptr; // Todo: add index to column auto index = strtoul(reader.column(0), &end, 10); std::string name = reader.column(1); // Todo: extract size auto type = (string2type(reader.column(2))); end = nullptr; utils::constraints options{}; if (strtoul(reader.column(3), &end, 10) == 0) { options = utils::constraints::NOT_NULL; } // f.default_value(res->column(4)); prototype.append({name, type, {options}}); } return std::move(prototype); } bool sqlite_connection::exists(const std::string &table_name) { const auto result = fetch_internal("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1"); sqlite_result_reader reader(std::move(result.rows), result.prototype.size()); if (!reader.fetch()) { // Todo: throw an exception? return false; } int v{}; reader.read_value(nullptr, 0, v); return v == 1; } } extern "C" { MATADOR_SQLITE_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) { return new matador::backends::sqlite::sqlite_connection(info); } MATADOR_SQLITE_API void destroy_database(matador::sql::connection_impl *db) { delete db; } }