Files
query/backends/sqlite/src/sqlite_connection.cpp
T

192 lines
4.9 KiB
C++

#include "sqlite_connection.hpp"
#include "sqlite_error.hpp"
#include "sqlite_query_result.hpp"
#include "matador/sql/record.hpp"
#include <cstring>
#include <memory>
#include <utility>
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<fetch_context*>(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<sql::query_result_impl> 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<sqlite_query_result>(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;
}
}