progress on record fetch

This commit is contained in:
Sascha Kuehl 2023-11-20 07:15:53 +01:00
parent ae5777bdb0
commit 171e8d36ce
20 changed files with 174 additions and 63 deletions

View File

@ -2,6 +2,7 @@
#define QUERY_SQLITE_QUERY_RESULT_HPP #define QUERY_SQLITE_QUERY_RESULT_HPP
#include "matador/sql/query_result_impl.hpp" #include "matador/sql/query_result_impl.hpp"
#include "matador/sql/record.hpp"
#include <vector> #include <vector>
@ -9,8 +10,15 @@ namespace matador::backends::sqlite {
class sqlite_query_result : public sql::query_result_impl { class sqlite_query_result : public sql::query_result_impl {
public: public:
using columns = std::vector<char*>;
using rows = std::vector<columns>;
public:
sqlite_query_result(sql::record prototype, rows result);
~sqlite_query_result() override; ~sqlite_query_result() override;
size_t column_count() const override;
void read_value(const char *id, size_t index, char &value) override; void read_value(const char *id, size_t index, char &value) override;
void read_value(const char *id, size_t index, short &value) override; void read_value(const char *id, size_t index, short &value) override;
void read_value(const char *id, size_t index, int &value) override; void read_value(const char *id, size_t index, int &value) override;
@ -39,9 +47,6 @@ private:
void push_back(char **row_values, int column_count); void push_back(char **row_values, int column_count);
private: private:
using columns = std::vector<char*>;
using rows = std::vector<columns>;
rows result_; rows result_;
long long row_index_ = -1; long long row_index_ = -1;
}; };

View File

@ -37,14 +37,37 @@ bool sqlite_connection::is_open()
return sqlite_db_ != nullptr; 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) int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns)
{ {
auto *result = static_cast<sqlite_query_result*>(param); auto *context = static_cast<fetch_context*>(param);
result->push_back(values, column_count);
sql::record prototype; sqlite_query_result::columns column;
for(int i = 0; i < column_count; ++i) { for(int i = 0; i < column_count; ++i) {
prototype.append(sql::column{columns[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; return 0;
@ -62,13 +85,13 @@ size_t sqlite_connection::execute(const std::string &stmt)
std::unique_ptr<sql::query_result_impl> sqlite_connection::fetch(const std::string &stmt) std::unique_ptr<sql::query_result_impl> sqlite_connection::fetch(const std::string &stmt)
{ {
auto result = std::make_unique<sqlite_query_result>(); fetch_context context;
char *errmsg = nullptr; char *errmsg = nullptr;
const int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), parse_result, result.get(), &errmsg); const int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), parse_result, &context, &errmsg);
throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt); throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt);
return std::move(result); 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) void sqlite_connection::prepare(const std::string &stmt)

View File

@ -4,6 +4,8 @@
#include <cstring> #include <cstring>
#include <stdexcept> #include <stdexcept>
#include <sqlite3.h>
namespace matador::backends::sqlite { namespace matador::backends::sqlite {
template < class Type > template < class Type >
@ -48,6 +50,11 @@ void read(Type &x, const char *val, typename std::enable_if<std::is_floating_poi
} }
} }
sqlite_query_result::sqlite_query_result(sql::record prototype, sqlite_query_result::rows result)
: sql::query_result_impl(std::move(prototype))
, result_(std::move(result))
{}
sqlite_query_result::~sqlite_query_result() sqlite_query_result::~sqlite_query_result()
{ {
std::for_each(result_.begin(), result_.end(), [](rows ::value_type& row) { std::for_each(result_.begin(), result_.end(), [](rows ::value_type& row) {
@ -57,6 +64,11 @@ sqlite_query_result::~sqlite_query_result()
}); });
} }
size_t sqlite_query_result::column_count() const
{
return prototype_.size();
}
void sqlite_query_result::read_value(const char *id, size_t index, char &value) void sqlite_query_result::read_value(const char *id, size_t index, char &value)
{ {
read(value, result_[row_index_][index]); read(value, result_[row_index_][index]);
@ -265,5 +277,4 @@ void sqlite_query_result::read_value(const char *id, size_t index, sql::any_type
} }
} }

View File

@ -2,7 +2,6 @@
#define QUERY_COLUMN_GENERATOR_HPP #define QUERY_COLUMN_GENERATOR_HPP
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/table_repository.hpp"
#include "matador/sql/types.hpp" #include "matador/sql/types.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
@ -13,6 +12,8 @@
namespace matador::sql { namespace matador::sql {
class table_repository;
class fk_column_generator class fk_column_generator
{ {
public: public:

View File

@ -29,17 +29,23 @@ public:
[[nodiscard]] bool is_open() const; [[nodiscard]] bool is_open() const;
[[nodiscard]] const connection_info& info() const; [[nodiscard]] const connection_info& info() const;
[[nodiscard]] record describe(const std::string &table_name) const;
template<class Type> template<class Type>
query_result<Type> fetch(const std::string &sql) query_result<Type> fetch(const std::string &sql)
{ {
return query_result<Type>(connection_->execute(sql)); return query_result<Type>(connection_->fetch(sql));
} }
query_result<record> fetch(const std::string &sql); [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
std::pair<size_t, std::string> execute(const std::string &sql); [[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
private:
friend class session;
[[nodiscard]] std::unique_ptr<query_result_impl> call_fetch(const std::string &sql) const;
private: private:
connection_info connection_info_; connection_info connection_info_;
bool is_open_{false};
std::unique_ptr<connection_impl> connection_; std::unique_ptr<connection_impl> connection_;
}; };

View File

@ -54,6 +54,13 @@ enum class join_type_t {
INNER, OUTER, LEFT, RIGHT INNER, OUTER, LEFT, RIGHT
}; };
struct query
{
std::string sql;
record prototype;
std::vector<any_type> host_vars;
};
class query_builder class query_builder
{ {
private: private:
@ -123,8 +130,6 @@ public:
std::string compile(); std::string compile();
[[nodiscard]] const record& prototype() const;
private: private:
void transition_to(state_t next); void transition_to(state_t next);
void initialize(command_t cmd, state_t state); void initialize(command_t cmd, state_t state);
@ -139,7 +144,7 @@ private:
detail::any_type_to_string_visitor value_to_string_; detail::any_type_to_string_visitor value_to_string_;
record prototype_; query query_;
using query_state_set = std::unordered_set<state_t>; using query_state_set = std::unordered_set<state_t>;
using query_state_transition_map = std::unordered_map<state_t, query_state_set>; using query_state_transition_map = std::unordered_map<state_t, query_state_set>;

View File

@ -47,6 +47,12 @@ protected:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
public: public:
template < class Type >
query_result<Type> fetch_all()
{
return query_result<Type>(fetch());
}
query_result<record> fetch_all(); query_result<record> fetch_all();
record fetch_one(); record fetch_one();
template<typename Type> template<typename Type>
@ -55,6 +61,9 @@ public:
auto result = fetch_all(); auto result = fetch_all();
return {}; return {};
} }
private:
std::unique_ptr<query_result_impl> fetch();
}; };
class query_limit_intermediate : public query_select_finish class query_limit_intermediate : public query_select_finish
@ -124,7 +133,6 @@ public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
query_from_intermediate from(const std::string &table, const std::string &as = ""); query_from_intermediate from(const std::string &table, const std::string &as = "");
}; };
class query_into_intermediate : public query_intermediate class query_into_intermediate : public query_intermediate
@ -149,7 +157,7 @@ public:
template<class Type> template<class Type>
query_execute_finish table(const std::string &table_name) query_execute_finish table(const std::string &table_name)
{ {
const auto &info = repository_.attach<Type>(table_name, record{column_generator::generate<Type>(repository_)}); const auto &info = repository_.attach<Type>(table_name/*, record{column_generator::generate<Type>(repository_)}*/);
return {db(), query().table(table_name, info.prototype.columns())}; return {db(), query().table(table_name, info.prototype.columns())};
} }

View File

@ -115,6 +115,7 @@ public:
public: public:
explicit query_result(std::unique_ptr<query_result_impl> impl) explicit query_result(std::unique_ptr<query_result_impl> impl)
: impl_(std::move(impl)) {} : impl_(std::move(impl)) {}
query_result(std::unique_ptr<query_result_impl> impl, creator_func creator) query_result(std::unique_ptr<query_result_impl> impl, creator_func creator)
: creator_(creator) : creator_(creator)
, impl_(std::move(impl)) {} , impl_(std::move(impl)) {}

View File

@ -5,6 +5,7 @@
#include "matador/utils/field_attributes.hpp" #include "matador/utils/field_attributes.hpp"
#include "matador/sql/any_type.hpp" #include "matador/sql/any_type.hpp"
#include "matador/sql/record.hpp"
#include "matador/sql/types.hpp" #include "matador/sql/types.hpp"
#include <string> #include <string>
@ -16,6 +17,8 @@ class query_result_impl
public: public:
virtual ~query_result_impl() = default; virtual ~query_result_impl() = default;
virtual size_t column_count() const = 0;
virtual void read_value(const char *id, size_t index, char &value) = 0; virtual void read_value(const char *id, size_t index, char &value) = 0;
virtual void read_value(const char *id, size_t index, short &value) = 0; virtual void read_value(const char *id, size_t index, short &value) = 0;
virtual void read_value(const char *id, size_t index, int &value) = 0; virtual void read_value(const char *id, size_t index, int &value) = 0;
@ -79,8 +82,14 @@ public:
[[nodiscard]] virtual const char* column(size_t index) const = 0; [[nodiscard]] virtual const char* column(size_t index) const = 0;
[[nodiscard]] virtual bool fetch() = 0; [[nodiscard]] virtual bool fetch() = 0;
const record& prototype() const;
protected:
explicit query_result_impl(record prototype);
protected: protected:
size_t column_index_ = 0; size_t column_index_ = 0;
record prototype_;
}; };
} }

View File

@ -29,8 +29,8 @@ public:
query_update_intermediate update(const std::string &table); query_update_intermediate update(const std::string &table);
query_delete_intermediate remove(); query_delete_intermediate remove();
query_result<record> fetch(const std::string &sql); [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
std::pair<size_t, std::string> execute(const std::string &sql); [[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
template<typename Type> template<typename Type>
void attach(const std::string &table_name) void attach(const std::string &table_name)
@ -40,6 +40,11 @@ public:
[[nodiscard]] const table_repository& tables() const; [[nodiscard]] const table_repository& tables() const;
private:
friend class query_select_finish;
[[nodiscard]] std::unique_ptr<query_result_impl> call_fetch(const std::string &sql) const;
private: private:
connection_pool<connection> &pool_; connection_pool<connection> &pool_;
query_builder query_; query_builder query_;

View File

@ -1,6 +1,7 @@
#ifndef QUERY_TABLE_REPOSITORY_HPP #ifndef QUERY_TABLE_REPOSITORY_HPP
#define QUERY_TABLE_REPOSITORY_HPP #define QUERY_TABLE_REPOSITORY_HPP
#include "matador/sql/column_generator.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include <optional> #include <optional>
@ -20,12 +21,11 @@ class table_repository
{ {
public: public:
template<typename Type> template<typename Type>
const table_info& attach(const std::string &table_name, const record &proto) const table_info& attach(const std::string &table_name)
{ {
return attach(std::type_index(typeid(Type)), table_name, proto); return attach(std::type_index(typeid(Type)), table_info{table_name, record{column_generator::generate<Type>(*this)}});
} }
const table_info& attach(std::type_index ti, const std::string &table_name, const record &proto);
const table_info& attach(std::type_index ti, const table_info& table); const table_info& attach(std::type_index ti, const table_info& table);
template<typename Type> template<typename Type>

View File

@ -1,4 +1,5 @@
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/table_repository.hpp"
namespace matador::sql { namespace matador::sql {

View File

@ -63,14 +63,25 @@ const connection_info &connection::info() const
return connection_info_; return connection_info_;
} }
query_result<record> connection::fetch(const std::string &sql) record connection::describe(const std::string &table_name) const
{
return std::move(connection_->describe(table_name));
}
query_result<record> connection::fetch(const std::string &sql) const
{ {
auto rec = connection_->describe("person"); auto rec = connection_->describe("person");
return {connection_->fetch(sql), [rec](){ return new record(rec); }}; return {connection_->fetch(sql), [rec](){ return new record(rec); }};
} }
std::pair<size_t, std::string> connection::execute(const std::string &sql) std::pair<size_t, std::string> connection::execute(const std::string &sql) const
{ {
return {connection_->execute(sql), sql}; return {connection_->execute(sql), sql};
} }
std::unique_ptr<query_result_impl> connection::call_fetch(const std::string &sql) const
{
return connection_->fetch(sql);
}
} }

View File

@ -108,13 +108,13 @@ query_builder& query_builder::select(const std::vector<std::string> &column_name
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " "); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " ");
prototype_.clear(); query_.prototype.clear();
std::string result; std::string result;
if (column_names.size() < 2) { if (column_names.size() < 2) {
for (const auto &col : column_names) { for (const auto &col : column_names) {
result.append(dialect_.prepare_identifier(col)); result.append(dialect_.prepare_identifier(col));
prototype_.append(column{col}); query_.prototype.append(column{col});
} }
} else { } else {
auto it = column_names.begin(); auto it = column_names.begin();
@ -122,7 +122,7 @@ query_builder& query_builder::select(const std::vector<std::string> &column_name
for (; it != column_names.end(); ++it) { for (; it != column_names.end(); ++it) {
result.append(", "); result.append(", ");
result.append(dialect_.prepare_identifier(*it)); result.append(dialect_.prepare_identifier(*it));
prototype_.append(column{*it}); query_.prototype.append(column{*it});
} }
} }
@ -262,17 +262,20 @@ query_builder& query_builder::values(const std::vector<any_type> &values) {
std::string result{"("}; std::string result{"("};
if (values.size() < 2) { if (values.size() < 2) {
for (auto val : values) { for (auto val : values) {
query_.host_vars.push_back(val);
std::visit(value_to_string_, val); std::visit(value_to_string_, val);
result.append(value_to_string_.result); result.append(value_to_string_.result);
} }
} else { } else {
auto it = values.begin(); auto it = values.begin();
auto val = *it++; auto val = *it++;
query_.host_vars.push_back(val);
std::visit(value_to_string_, val); std::visit(value_to_string_, val);
result.append(value_to_string_.result); result.append(value_to_string_.result);
for (; it != values.end(); ++it) { for (; it != values.end(); ++it) {
result.append(", "); result.append(", ");
val = *it; val = *it;
query_.host_vars.push_back(val);
std::visit(value_to_string_, val); std::visit(value_to_string_, val);
result.append(value_to_string_.result); result.append(value_to_string_.result);
} }
@ -403,11 +406,6 @@ std::string query_builder::compile() {
return result; return result;
} }
const record& query_builder::prototype() const
{
return prototype_;
}
void query_builder::transition_to(query_builder::state_t next) void query_builder::transition_to(query_builder::state_t next)
{ {
if (transitions_[state_].count(next) == 0) { if (transitions_[state_].count(next) == 0) {
@ -419,6 +417,7 @@ void query_builder::transition_to(query_builder::state_t next)
void query_builder::initialize(query_builder::command_t cmd, query_builder::state_t state) void query_builder::initialize(query_builder::command_t cmd, query_builder::state_t state)
{ {
command_ = cmd; command_ = cmd;
query_ = {};
state_ = state; state_ = state;
query_parts_.clear(); query_parts_.clear();
} }
@ -432,11 +431,9 @@ std::string build_create_column(const column &col, const dialect &d, column_cont
result.append(" NOT NULL"); result.append(" NOT NULL");
} }
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
// result.append(" PRIMARY KEY");
context.primary_keys.emplace_back(col.name()); context.primary_keys.emplace_back(col.name());
} }
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) { if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
// result.append(" FOREIGN KEY");
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()}); context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
} }

View File

@ -22,6 +22,11 @@ record query_select_finish::fetch_one()
return *db().fetch(query().compile()).begin().get(); return *db().fetch(query().compile()).begin().get();
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch()
{
return db().call_fetch(query().compile());
}
query_intermediate::query_intermediate(session &db, query_builder &query) query_intermediate::query_intermediate(session &db, query_builder &query)
: db_(db), query_(query) {} : db_(db), query_(query) {}

View File

@ -28,4 +28,13 @@ query_result_impl::on_attribute(const char *id, any_type &value, data_type_t typ
read_value(id, column_index_++, value, type, attr.size()); read_value(id, column_index_++, value, type, attr.size());
} }
const record& query_result_impl::prototype() const
{
return prototype_;
}
query_result_impl::query_result_impl(record prototype)
: prototype_(std::move(prototype))
{}
} }

View File

@ -16,6 +16,11 @@ record::record(const std::vector<column> &columns)
init(); init();
} }
const std::vector<column> &record::columns() const
{
return columns_;
}
bool record::has_primary_key() const bool record::has_primary_key() const
{ {
return pk_index_ > -1; return pk_index_ > -1;
@ -30,11 +35,6 @@ const column &record::primary_key() const
return columns_[pk_index_]; return columns_[pk_index_];
} }
const std::vector<column> &record::columns() const
{
return columns_;
}
const column &record::at(const std::string &name) const const column &record::at(const std::string &name) const
{ {
return columns_by_name_.at(name).first; return columns_by_name_.at(name).first;
@ -113,7 +113,6 @@ void record::init()
size_t index{0}; size_t index{0};
for(auto &col : columns_) { for(auto &col : columns_) {
add_to_map(col, index++); add_to_map(col, index++);
// columns_by_name_.emplace(col.name(), column_index_pair {std::ref(col), index++});
} }
} }

View File

@ -22,6 +22,7 @@ query_drop_intermediate session::drop()
query_select_intermediate session::select(std::initializer_list<std::string> column_names) query_select_intermediate session::select(std::initializer_list<std::string> column_names)
{ {
return query_select_intermediate{*this, query_.select(column_names)}; return query_select_intermediate{*this, query_.select(column_names)};
} }
@ -40,15 +41,14 @@ query_delete_intermediate session::remove()
return query_delete_intermediate{*this, query_.remove()}; return query_delete_intermediate{*this, query_.remove()};
} }
query_result<record> session::fetch(const std::string &sql) { query_result<record> session::fetch(const std::string &sql) const
auto c = pool_.acquire(); {
if (!c.valid()) { auto res = call_fetch(sql);
throw std::logic_error("no database connection available"); auto proto = res->prototype();
} return query_result<record>{std::move(res), [proto]() { return new record(proto); }};
return c->fetch(sql);
} }
std::pair<size_t, std::string> session::execute(const std::string &sql) { std::pair<size_t, std::string> session::execute(const std::string &sql) const {
auto c = pool_.acquire(); auto c = pool_.acquire();
if (!c.valid()) { if (!c.valid()) {
throw std::logic_error("no database connection available"); throw std::logic_error("no database connection available");
@ -60,4 +60,14 @@ const table_repository& session::tables() const
{ {
return table_repository_; return table_repository_;
} }
std::unique_ptr<query_result_impl> session::call_fetch(const std::string &sql) const
{
auto c = pool_.acquire();
if (!c.valid()) {
throw std::logic_error("no database connection available");
}
return c->call_fetch(sql);
}
} }

View File

@ -4,11 +4,6 @@
namespace matador::sql { namespace matador::sql {
const table_info &table_repository::attach(std::type_index ti, const std::string &table_name, const record &proto)
{
return attach(ti, table_info{table_name, proto});
}
const table_info& table_repository::attach(const std::type_index ti, const table_info& table) const table_info& table_repository::attach(const std::type_index ti, const table_info& table)
{ {
return repository_.try_emplace(ti, table).first->second; return repository_.try_emplace(ti, table).first->second;

View File

@ -161,12 +161,13 @@ TEST_CASE("Execute select statement with where clause", "[session]") {
.execute(); .execute();
REQUIRE(res.first == 1); REQUIRE(res.first == 1);
auto result = s.select<person>() // fetch person as record
auto result_record = s.select<person>()
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
for (const auto& i : result) { for (const auto& i : result_record) {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
@ -179,10 +180,19 @@ TEST_CASE("Execute select statement with where clause", "[session]") {
REQUIRE(i.at(2).value<long long>() == george.age); REQUIRE(i.at(2).value<long long>() == george.age);
} }
// REQUIRE(res.str() == R"(SELECT "id", "name", "color" FROM "person" WHERE "id" = 8)"); // fetch person as person
auto result_person = s.select<person>()
.from("person")
.where("id"_col == 7)
.fetch_all<person>();
for (const auto& i : result_person) {
REQUIRE(i.id == 7);
REQUIRE(i.name == "george");
REQUIRE(i.age == 45);
}
s.drop().table("person").execute(); s.drop().table("person").execute();
} }
TEST_CASE("Execute select statement with order by", "[session]") { TEST_CASE("Execute select statement with order by", "[session]") {