#include "sqlite_connection.hpp" #include "sqlite_error.hpp" #include "sqlite_query_result.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() { const auto ret = sqlite3_open(info().database.c_str(), &sqlite_db_); if (ret != SQLITE_OK) { throw_sqlite_error(ret, sqlite_db_, "open"); } } void sqlite_connection::close() { int ret = sqlite3_close(sqlite_db_); throw_sqlite_error(ret, sqlite_db_, "close"); sqlite_db_ = nullptr; } bool sqlite_connection::is_open() { return sqlite_db_ != nullptr; } struct fetch_context { sql::record prototype; sqlite_query_result::rows rows; }; int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns) { auto *context = static_cast(param); sqlite_query_result::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; } size_t sqlite_connection::execute(const std::string &stmt) { char *errmsg = nullptr; int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), nullptr, nullptr, &errmsg); throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt); return sqlite3_changes(sqlite_db_); } std::unique_ptr sqlite_connection::fetch(const std::string &stmt) { fetch_context context; char *errmsg = nullptr; const int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), parse_result, &context, &errmsg); throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt); return std::move(std::make_unique(std::move(context.prototype), std::move(context.rows))); } void sqlite_connection::prepare(const std::string &stmt) { } 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) { std::string stmt("PRAGMA table_info(" + table + ")"); const auto result = fetch("PRAGMA table_info(" + table + ")"); sql::record prototype; while (result->fetch()) { char *end = nullptr; // Todo: add index to column auto index = strtoul(result->column(0), &end, 10); std::string name = result->column(1); // Todo: extract size auto type = (string2type(result->column(2))); end = nullptr; utils::constraints options{}; if (strtoul(result->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("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1"); if (!result->fetch()) { // Todo: throw an exception? return false; } int v{}; result->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; } }