diff --git a/backends/sqlite/include/sqlite_connection.hpp b/backends/sqlite/include/sqlite_connection.hpp index c5096e7..fdd02a0 100644 --- a/backends/sqlite/include/sqlite_connection.hpp +++ b/backends/sqlite/include/sqlite_connection.hpp @@ -31,6 +31,8 @@ public: size_t execute(const std::string &stmt) override; + sql::record describe(const std::string& table) override; + private: static int parse_result(void* param, int column_count, char** values, char** columns); diff --git a/backends/sqlite/include/sqlite_query_result.hpp b/backends/sqlite/include/sqlite_query_result.hpp index 5fcba19..071e4e6 100644 --- a/backends/sqlite/include/sqlite_query_result.hpp +++ b/backends/sqlite/include/sqlite_query_result.hpp @@ -9,6 +9,8 @@ namespace matador::backends::sqlite { class sqlite_query_result : public sql::query_result_impl { public: + ~sqlite_query_result() 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, int &value) override; @@ -25,9 +27,10 @@ public: void read_value(const char *id, size_t index, char *value, size_t size) override; void read_value(const char *id, size_t index, std::string &value) override; void read_value(const char *id, size_t index, std::string &value, size_t s) override; + void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override; -protected: - [[nodiscard]] bool next_row() override; + [[nodiscard]] const char* column(size_t index) const override; + [[nodiscard]] bool fetch() override; private: friend class sqlite_connection; @@ -40,8 +43,7 @@ private: using rows = std::vector; rows result_; - size_t row_index_ = 0; - + long long row_index_ = -1; }; } diff --git a/backends/sqlite/src/sqlite_connection.cpp b/backends/sqlite/src/sqlite_connection.cpp index 5e43bf0..9f39d85 100644 --- a/backends/sqlite/src/sqlite_connection.cpp +++ b/backends/sqlite/src/sqlite_connection.cpp @@ -2,9 +2,11 @@ #include "sqlite_error.hpp" #include "sqlite_query_result.hpp" -#include +#include "matador/sql/record.hpp" +#include #include +#include namespace matador::backends::sqlite { @@ -40,6 +42,11 @@ int sqlite_connection::parse_result(void* param, int column_count, char** values auto *result = static_cast(param); result->push_back(values, column_count); + sql::record prototype; + for(int i = 0; i < column_count; ++i) { + prototype.append(sql::column{columns[i]}); + } + return 0; } @@ -57,7 +64,7 @@ std::unique_ptr sqlite_connection::fetch(const std::stri { auto result = std::make_unique(); char *errmsg = nullptr; - 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, result.get(), &errmsg); throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt); @@ -68,6 +75,69 @@ 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)); + // end = nullptr; + // f.is_primary_key(strtoul(res->column(3), &end, 10) == 0); + prototype.append({name, type, {options}}); + } + + return std::move(prototype); +} + } extern "C" diff --git a/backends/sqlite/src/sqlite_query_result.cpp b/backends/sqlite/src/sqlite_query_result.cpp index 4af787b..31a1832 100644 --- a/backends/sqlite/src/sqlite_query_result.cpp +++ b/backends/sqlite/src/sqlite_query_result.cpp @@ -1,5 +1,6 @@ #include "sqlite_query_result.hpp" +#include #include #include @@ -13,7 +14,7 @@ void read(Type &x, const char *val, typename std::enable_if(strtoll(val, &end, 10)); - if (end != nullptr) { + if (end == nullptr) { // Todo: check error throw std::logic_error("couldn't convert value to number"); } @@ -27,7 +28,7 @@ void read(Type &x, const char *val, typename std::enable_if(strtoull(val, &end, 10)); - if (end != nullptr) { + if (end == nullptr) { // Todo: check error throw std::logic_error("couldn't convert value to number"); } @@ -41,12 +42,21 @@ void read(Type &x, const char *val, typename std::enable_if(strtold(val, &end)); - if (end != nullptr) { + if (end == nullptr) { // Todo: check error throw std::logic_error("couldn't convert value to number"); } } +sqlite_query_result::~sqlite_query_result() +{ + std::for_each(result_.begin(), result_.end(), [](rows ::value_type& row) { + std::for_each(row.begin(), row.end(), [](const char *val) { + delete [] val; + }); + }); +} + void sqlite_query_result::read_value(const char *id, size_t index, char &value) { read(value, result_[row_index_][index]); @@ -162,10 +172,81 @@ void sqlite_query_result::push_back(char **row_values, int column_count) result_.emplace_back(data); } -bool sqlite_query_result::next_row() +const char* sqlite_query_result::column(size_t index) const { - column_index_ = 0; - return row_index_++ < result_.size(); + return result_[row_index_][index]; } +bool sqlite_query_result::fetch() +{ + column_index_ = 0; + return ++row_index_ < result_.size(); +} + +void sqlite_query_result::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) +{ + switch (type) { + case sql::data_type_t::type_char: + case sql::data_type_t::type_short: + case sql::data_type_t::type_int: + case sql::data_type_t::type_long: + case sql::data_type_t::type_long_long: { + long long val{}; + read(val, result_[row_index_][index]); + value = val; + break; + } + case sql::data_type_t::type_unsigned_char: + case sql::data_type_t::type_unsigned_short: + case sql::data_type_t::type_unsigned_int: + case sql::data_type_t::type_unsigned_long: + case sql::data_type_t::type_unsigned_long_long: { + unsigned long long val{}; + read(val, result_[row_index_][index]); + value = val; + break; + } + case sql::data_type_t::type_float: + case sql::data_type_t::type_double: { + double val{}; + read(val, result_[row_index_][index]); + value = val; + break; + } + case sql::data_type_t::type_bool: { + int val{}; + read(val, result_[row_index_][index]); + value = val > 0; + break; + } + case sql::data_type_t::type_text: + case sql::data_type_t::type_varchar: { + value = std::string{result_[row_index_][index]}; + break; + } + case sql::data_type_t::type_char_pointer: { + value = result_[row_index_][index]; + break; + } + case sql::data_type_t::type_time: + case sql::data_type_t::type_date: { + value = std::string{result_[row_index_][index]}; + break; + } + case sql::data_type_t::type_null: { + value = nullptr_t{}; + break; + } + case sql::data_type_t::type_blob: { + throw std::logic_error("data type blob not supported"); + } + case sql::data_type_t::type_unknown: { + value = result_[row_index_][index]; + break; + } + } + +} + + } \ No newline at end of file diff --git a/include/matador/sql/column.hpp b/include/matador/sql/column.hpp index b77fe98..abe15fb 100644 --- a/include/matador/sql/column.hpp +++ b/include/matador/sql/column.hpp @@ -38,6 +38,15 @@ public: [[nodiscard]] const std::string& ref_column() const; private: + template + void process(Operator &op) + { + op.on_attribute(name_.c_str(), value_, type_, attributes_); + } + +private: + friend class record; + std::string name_; utils::field_attributes attributes_; data_type_t type_{}; diff --git a/include/matador/sql/column_generator.hpp b/include/matador/sql/column_generator.hpp index 738fe4d..890e335 100644 --- a/include/matador/sql/column_generator.hpp +++ b/include/matador/sql/column_generator.hpp @@ -10,29 +10,38 @@ #include -namespace matador::utils { -class identifiable; -} - namespace matador::sql { -class fk_column_generator : public utils::identifier_serializer +class fk_column_generator { public: fk_column_generator() = default; - column generate(const char *id, utils::identifiable &x); + template + column generate(const char *id, Type &x) + { + utils::access::process(*this, x); + return column{id, type_, { utils::constraints::FOREIGN_KEY }}; + } - void serialize(short &i, const utils::field_attributes &attributes) override; - void serialize(int &i, const utils::field_attributes &attributes) override; - void serialize(long &i, const utils::field_attributes &attributes) override; - void serialize(long long int &i, const utils::field_attributes &attributes) override; - void serialize(unsigned short &i, const utils::field_attributes &attributes) override; - void serialize(unsigned int &i, const utils::field_attributes &attributes) override; - void serialize(unsigned long &i, const utils::field_attributes &attributes) override; - void serialize(unsigned long long int &i, const utils::field_attributes &attributes) override; - void serialize(std::string &string, const utils::field_attributes &attributes) override; - void serialize(utils::null_type_t &type, const utils::field_attributes &attributes) override; + template + void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if::value && !std::is_same::value>::type* = 0) + { + type_ = data_type_traits::builtin_type(0); + } + void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size); + void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} + template < class Type > + void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template + void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} + template + void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} + template + void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} + template + void on_has_many(const char *, ContainerType &, utils::cascade_type) {} private: data_type_t type_{}; @@ -64,8 +73,16 @@ public: template void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes); - void on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type); - void on_has_one(const char *id, utils::identifiable &x, utils::cascade_type); + template + void on_belongs_to(const char *id, Pointer &x, utils::cascade_type) + { + columns_.push_back(fk_column_generator_.generate(id, *x)); + } + template + void on_has_one(const char *id, Pointer &x, utils::cascade_type) + { + columns_.push_back(fk_column_generator_.generate(id, *x)); + } template void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} template diff --git a/include/matador/sql/column_name_generator.hpp b/include/matador/sql/column_name_generator.hpp index 3fbe193..a36bbcf 100644 --- a/include/matador/sql/column_name_generator.hpp +++ b/include/matador/sql/column_name_generator.hpp @@ -7,10 +7,6 @@ #include #include -namespace matador::utils { -class identifiable; -} - namespace matador::sql { class column_name_generator @@ -45,8 +41,16 @@ public: column_names_.emplace_back(id); } - void on_belongs_to(const char *id, utils::identifiable &, utils::cascade_type); - void on_has_one(const char *id, utils::identifiable &, utils::cascade_type); + template + void on_belongs_to(const char *id, Pointer &, utils::cascade_type) + { + column_names_.emplace_back(id); + } + template + void on_has_one(const char *id, Pointer &, utils::cascade_type) + { + column_names_.emplace_back(id); + } template void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} template diff --git a/include/matador/sql/connection_impl.hpp b/include/matador/sql/connection_impl.hpp index 98cb222..9b0ee1c 100644 --- a/include/matador/sql/connection_impl.hpp +++ b/include/matador/sql/connection_impl.hpp @@ -3,6 +3,7 @@ #include "matador/sql/connection_info.hpp" #include "matador/sql/query_result_impl.hpp" +#include "matador/sql/record.hpp" #include @@ -23,6 +24,8 @@ public: virtual std::unique_ptr fetch(const std::string &stmt) = 0; virtual void prepare(const std::string &stmt) = 0; + virtual record describe(const std::string &table) = 0; + protected: explicit connection_impl(const connection_info &info); diff --git a/include/matador/sql/foreign.hpp b/include/matador/sql/foreign.hpp index 2f5f96d..f8700b4 100644 --- a/include/matador/sql/foreign.hpp +++ b/include/matador/sql/foreign.hpp @@ -1,38 +1,18 @@ #ifndef QUERY_FOREIGN_HPP #define QUERY_FOREIGN_HPP -#include "matador/utils/identifiable.hpp" +#include namespace matador::sql { template < class Type > -class foreign : public utils::identifiable +class foreign { public: foreign() = default; explicit foreign(Type *obj) : obj_(obj) {} - void reset(const utils::identifier &id) override { - id_ = id; - } - - [[nodiscard]] bool has_primary_key() const override { - return true; - } - - [[nodiscard]] const utils::identifier &primary_key() const override { - return id_; - } - - utils::identifier &primary_key() override { - return id_; - } - - [[nodiscard]] utils::identifier create_identifier() const override { - return {id_}; - } - Type* operator->() { return obj_.get(); } const Type* operator->() const { return obj_.get(); } @@ -40,7 +20,6 @@ public: const Type& operator*() const { return *obj_; } private: - utils::identifier id_; std::unique_ptr obj_; }; diff --git a/include/matador/sql/key_value_generator.hpp b/include/matador/sql/key_value_generator.hpp index 5ca0243..6d1204b 100644 --- a/include/matador/sql/key_value_generator.hpp +++ b/include/matador/sql/key_value_generator.hpp @@ -6,10 +6,6 @@ #include -namespace matador::utils { -class identifiable; -} - namespace matador::sql { class key_value_generator diff --git a/include/matador/sql/query_builder.hpp b/include/matador/sql/query_builder.hpp index 021acb4..1935413 100644 --- a/include/matador/sql/query_builder.hpp +++ b/include/matador/sql/query_builder.hpp @@ -4,12 +4,14 @@ #include "matador/sql/basic_condition.hpp" #include "matador/sql/column.hpp" #include "matador/sql/key_value_pair.hpp" +#include "matador/sql/record.hpp" #include #include #include #include + namespace matador::sql { class dialect; @@ -121,6 +123,8 @@ public: std::string compile(); + [[nodiscard]] const record& prototype() const; + private: void transition_to(state_t next); void initialize(command_t cmd, state_t state); @@ -135,6 +139,8 @@ private: detail::any_type_to_string_visitor value_to_string_; + record prototype_; + using query_state_set = std::unordered_set; using query_state_transition_map = std::unordered_map; using query_state_to_string_map = std::unordered_map; diff --git a/include/matador/sql/query_result.hpp b/include/matador/sql/query_result.hpp index 6c1674a..847122b 100644 --- a/include/matador/sql/query_result.hpp +++ b/include/matador/sql/query_result.hpp @@ -1,6 +1,8 @@ #ifndef QUERY_QUERY_RESULT_HPP #define QUERY_QUERY_RESULT_HPP +#include + #include "matador/sql/query_result_impl.hpp" #include @@ -23,10 +25,10 @@ public: public: query_result_iterator() = default; - explicit query_result_iterator(query_result &res) + explicit query_result_iterator(query_result *res) : result_(res) {} - query_result_iterator(query_result &res, std::unique_ptr obj) + query_result_iterator(query_result *res, std::unique_ptr obj) : obj_(std::move(obj)) , result_(res) {} @@ -56,9 +58,9 @@ public: self& operator++() { - obj_.reset(result_.create()); - result_.bind(*obj_); - if (!result_.fetch(*obj_)) { + obj_.reset(result_->create()); + result_->bind(*obj_); + if (!result_->fetch(*obj_)) { obj_.reset(); } @@ -69,9 +71,9 @@ public: { const self tmp(result_, obj_); - obj_.reset(result_.create()); - result_.bind(*obj_); - if (!result_.fetch(*obj_)) { + obj_.reset(result_->create()); + result_->bind(*obj_); + if (!result_->fetch(*obj_)) { obj_.reset(); } @@ -100,7 +102,7 @@ public: private: std::unique_ptr obj_; - query_result &result_; + query_result *result_{nullptr}; }; template < typename Type > @@ -108,18 +110,22 @@ class query_result { public: using iterator = query_result_iterator; + using creator_func = std::function; public: explicit query_result(std::unique_ptr impl) : impl_(std::move(impl)) {} + query_result(std::unique_ptr impl, creator_func creator) + : creator_(creator) + , impl_(std::move(impl)) {} - iterator begin() { return std::move(++iterator(*this)); } + iterator begin() { return std::move(++iterator(this)); } iterator end() { return {}; } private: friend class query_result_iterator; - Type* create() { return new Type; } + Type* create() { return creator_(); } void bind(const Type &obj) { @@ -132,6 +138,7 @@ private: } private: + creator_func creator_ = []{ return new Type; }; std::unique_ptr impl_; }; diff --git a/include/matador/sql/query_result_impl.hpp b/include/matador/sql/query_result_impl.hpp index d12bd91..137a796 100644 --- a/include/matador/sql/query_result_impl.hpp +++ b/include/matador/sql/query_result_impl.hpp @@ -4,11 +4,10 @@ #include "matador/utils/access.hpp" #include "matador/utils/field_attributes.hpp" -#include +#include "matador/sql/any_type.hpp" +#include "matador/sql/types.hpp" -namespace matador::utils { -class identifiable; -} +#include namespace matador::sql { @@ -35,6 +34,7 @@ public: virtual void read_value(const char *id, size_t index, char *value, size_t s) = 0; virtual void read_value(const char *id, size_t index, std::string &value) = 0; virtual void read_value(const char *id, size_t index, std::string &value, size_t s) = 0; + virtual void read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size) = 0; template void on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0) @@ -51,9 +51,12 @@ public: } void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); + void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes); - void on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type); - void on_has_one(const char *id, utils::identifiable &x, utils::cascade_type); + template < class Pointer > + void on_belongs_to(const char *id, Pointer &x, utils::cascade_type) {} + template < class Pointer > + void on_has_one(const char *id, Pointer &x, utils::cascade_type) {} template void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} @@ -66,15 +69,15 @@ public: template bool fetch(Type &obj) { - if (!next_row()) { + if (!fetch()) { return false; } matador::utils::access::process(*this, obj); return true; } -protected: - [[nodiscard]] virtual bool next_row() = 0; + [[nodiscard]] virtual const char* column(size_t index) const = 0; + [[nodiscard]] virtual bool fetch() = 0; protected: size_t column_index_ = 0; diff --git a/include/matador/sql/record.hpp b/include/matador/sql/record.hpp index 2903f06..881683a 100644 --- a/include/matador/sql/record.hpp +++ b/include/matador/sql/record.hpp @@ -23,15 +23,17 @@ public: record() = default; record(std::initializer_list columns); + record(const record&) = default; + record& operator=(const record&) = default; + record(record&&) noexcept = default; + record& operator=(record&&) noexcept = default; ~record() = default; template void process(Operator &op) { for(auto &col : columns_) { - // Todo: Find a solution to handle a column with process - int todo{}; - matador::utils::access::attribute(op, col.name().c_str(), todo, col.attributes()); + col.process(op); } } template < typename Type > @@ -43,10 +45,10 @@ public: void append(column col); [[nodiscard]] const column& at(const std::string &name) const; - const column& at(size_t index); + const column& at(size_t index) const; iterator find(const std::string &column_name); - const_iterator find(const std::string &column_name) const; + [[nodiscard]] const_iterator find(const std::string &column_name) const; iterator begin(); [[nodiscard]] const_iterator begin() const; @@ -57,6 +59,8 @@ public: [[nodiscard]] const_iterator cend() const; [[nodiscard]] size_t size() const; + [[nodiscard]] bool empty() const; + void clear(); private: column_by_index columns_; diff --git a/include/matador/utils/constraints.hpp b/include/matador/utils/constraints.hpp index e8555f7..95c5327 100644 --- a/include/matador/utils/constraints.hpp +++ b/include/matador/utils/constraints.hpp @@ -26,6 +26,9 @@ inline constraints operator&(constraints a, constraints b) return static_cast(static_cast(a) & static_cast(b)); } +inline constraints& operator|= (constraints& a, constraints b) { return (constraints&)((int&)a |= (int)b); } +inline constraints& operator&= (constraints& a, constraints b) { return (constraints&)((int&)a &= (int)b); } + inline bool is_constraint_set(constraints source, constraints needle) { return static_cast(source & needle) > 0; diff --git a/include/matador/utils/identifiable.hpp b/include/matador/utils/identifiable.hpp deleted file mode 100644 index f28a10f..0000000 --- a/include/matador/utils/identifiable.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef QUERY_IDENTIFIABLE_HPP -#define QUERY_IDENTIFIABLE_HPP - -#include "matador/utils/identifier.hpp" - -namespace matador::utils { - -/** - * Base class for all pointer object - * which can contain an identifiable - */ -class identifiable -{ -public: - virtual ~identifiable() = default; - - /** - * Resets the object_holder with the given - * identifier. If the type of identifier differs - * from internal type an exception is thrown - * - * @param id The identifier to set - */ - virtual void reset(const identifier &id) = 0; - - /** - * Returns true if serializable has a primary key - * - * @return true if serializable has a primary key - */ - [[nodiscard]] virtual bool has_primary_key() const = 0; - - /** - * Gets the primary key of the foreign serializable - * - * @return The primary key of the foreign serializable - */ - [[nodiscard]] virtual const identifier& primary_key() const = 0; - virtual identifier& primary_key() = 0; - - /** - * Creates a new identifier object. - * - * @return Returns a new identifier object. - */ - [[nodiscard]] virtual identifier create_identifier() const = 0; -}; - -} -#endif //QUERY_IDENTIFIABLE_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37cb77d..2f9a399 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,7 +51,6 @@ set(UTILS_HEADER ../include/matador/utils/library.hpp ../include/matador/utils/os.hpp ../include/matador/utils/access.hpp - ../include/matador/utils/identifiable.hpp ../include/matador/utils/identifier.hpp ../include/matador/utils/cascade_type.hpp) diff --git a/src/sql/column_generator.cpp b/src/sql/column_generator.cpp index d93faf4..8f1b36e 100644 --- a/src/sql/column_generator.cpp +++ b/src/sql/column_generator.cpp @@ -1,7 +1,5 @@ #include "matador/sql/column_generator.hpp" -#include "matador/utils/identifiable.hpp" - namespace matador::sql { column_generator::column_generator(std::vector &columns) @@ -17,80 +15,9 @@ void column_generator::on_revision(const char *id, unsigned long long int &x) on_attribute(id, x); } -void column_generator::on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type) +void fk_column_generator::on_primary_key(const char *, std::string &, size_t size) { - columns_.push_back(fk_column_generator_.generate(id, x)); + type_ = data_type_traits::builtin_type(size); } -void column_generator::on_has_one(const char *id, utils::identifiable &x, utils::cascade_type) -{ - columns_.push_back(fk_column_generator_.generate(id, x)); -} - -column fk_column_generator::generate(const char *id, utils::identifiable &x) -{ - x.primary_key().serialize(*this); - return column{id, type_, { utils::constraints::FOREIGN_KEY }}; -} - -template < typename Type > -data_type_t determine_data_type(const Type &) -{ - return data_type_traits::builtin_type(0); -} - -data_type_t determine_data_type(const std::string&, size_t size) -{ - return data_type_traits::builtin_type(size); -} - -void fk_column_generator::serialize(short &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(int &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(long &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(long long int &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(unsigned short &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(unsigned int &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(unsigned long &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(unsigned long long int &i, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(i); -} - -void fk_column_generator::serialize(std::string &x, const utils::field_attributes &attributes) -{ - type_ = determine_data_type(x, attributes.size()); -} - -void fk_column_generator::serialize(utils::null_type_t &type, const utils::field_attributes &attributes) -{ - type_ = data_type_t::type_null; -} } \ No newline at end of file diff --git a/src/sql/column_name_generator.cpp b/src/sql/column_name_generator.cpp index 920e074..ac59d78 100644 --- a/src/sql/column_name_generator.cpp +++ b/src/sql/column_name_generator.cpp @@ -16,14 +16,4 @@ void column_name_generator::on_revision(const char *id, unsigned long long int & column_names_.emplace_back(id); } -void column_name_generator::on_belongs_to(const char *id, utils::identifiable &, utils::cascade_type) -{ - column_names_.emplace_back(id); -} - -void column_name_generator::on_has_one(const char *id, utils::identifiable &, utils::cascade_type) -{ - column_names_.emplace_back(id); -} - } \ No newline at end of file diff --git a/src/sql/connection.cpp b/src/sql/connection.cpp index c9c43ee..bcda76d 100644 --- a/src/sql/connection.cpp +++ b/src/sql/connection.cpp @@ -65,7 +65,8 @@ const connection_info &connection::info() const query_result connection::fetch(const std::string &sql) { - return query_result(connection_->fetch(sql)); + auto rec = connection_->describe("person"); + return {connection_->fetch(sql), [rec](){ return new record(rec); }}; } std::pair connection::execute(const std::string &sql) diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp index 02b4408..1619ddd 100644 --- a/src/sql/query_builder.cpp +++ b/src/sql/query_builder.cpp @@ -97,27 +97,32 @@ query_builder& query_builder::drop() { return *this; } -query_builder& query_builder::select(std::initializer_list column_names) { - initialize(command_t::SELECT, state_t::QUERY_SELECT); +query_builder& query_builder::select(std::initializer_list column_names) +{ + initialize(command_t::SELECT, state_t::QUERY_SELECT); - query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " "); + query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " "); - std::string result; - if (column_names.size() < 2) { - for (const auto &col : column_names) { - result.append(dialect_.prepare_identifier(col)); - } - } else { - auto it = column_names.begin(); - result.append(dialect_.prepare_identifier(*it++)); - for (; it != column_names.end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier(*it)); - } + prototype_.clear(); + + std::string result; + if (column_names.size() < 2) { + for (const auto &col : column_names) { + result.append(dialect_.prepare_identifier(col)); + prototype_.append(column{col}); } + } else { + auto it = column_names.begin(); + result.append(dialect_.prepare_identifier(*it++)); + for (; it != column_names.end(); ++it) { + result.append(", "); + result.append(dialect_.prepare_identifier(*it)); + prototype_.append(column{*it}); + } + } - query_parts_.emplace_back(result); - return *this; + query_parts_.emplace_back(result); + return *this; } query_builder& query_builder::insert() { @@ -393,6 +398,11 @@ std::string query_builder::compile() { return result; } +const record& query_builder::prototype() const +{ + return prototype_; +} + void query_builder::transition_to(query_builder::state_t next) { if (transitions_[state_].count(next) == 0) { diff --git a/src/sql/query_result_impl.cpp b/src/sql/query_result_impl.cpp index 15fffb6..f0f199c 100644 --- a/src/sql/query_result_impl.cpp +++ b/src/sql/query_result_impl.cpp @@ -22,14 +22,10 @@ void query_result_impl::on_attribute(const char *id, std::string &value, const u read_value(id, column_index_++, value, attr.size()); } -void query_result_impl::on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type) +void +query_result_impl::on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr) { - -} - -void query_result_impl::on_has_one(const char *id, utils::identifiable &x, utils::cascade_type) -{ - + read_value(id, column_index_++, value, type, attr.size()); } } \ No newline at end of file diff --git a/src/sql/record.cpp b/src/sql/record.cpp index 5d4961f..c05a133 100644 --- a/src/sql/record.cpp +++ b/src/sql/record.cpp @@ -15,7 +15,7 @@ const column &record::at(const std::string &name) const return columns_by_name_.at(name).first; } -const column &record::at(size_t index) +const column &record::at(size_t index) const { return columns_.at(index); } @@ -72,4 +72,15 @@ size_t record::size() const return columns_.size(); } -} \ No newline at end of file +bool record::empty() const +{ + return columns_.empty(); +} + +void record::clear() +{ + columns_.clear(); + columns_by_name_.clear(); +} + +} diff --git a/test/session.cpp b/test/session.cpp index bf7d522..e8faea4 100644 --- a/test/session.cpp +++ b/test/session.cpp @@ -9,7 +9,7 @@ using namespace matador::sql; using namespace matador::test; -TEST_CASE("Execute create table statement", "[session]") { +TEST_CASE("Execute create and drop table statement", "[session]") { connection_pool pool("sqlite://sqlite.db", 4); session s(pool); @@ -22,30 +22,81 @@ TEST_CASE("Execute create table statement", "[session]") { REQUIRE(res.second == R"(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255), "age" INTEGER, CONSTRAINT PK_person PRIMARY KEY (id)))"); - REQUIRE(s.drop().table("person").execute().first == 0); -// REQUIRE(res.first == 1); + res = s.drop().table("person").execute(); -// res = s.create().table("product").execute(); - -// REQUIRE(res.second == R"(CREATE TABLE "product" ("product_name" VARCHAR(255) PRIMARY KEY, "supplier_id" BIGINT NOT NULL FOREIGN KEY, "category_id" BIGINT NOT NULL FOREIGN KEY, "quantity_per_unit" VARCHAR(255), "unit_price" BIGINT, "units_in_stock" BIGINT, "units_in_order" BIGINT, "reorder_level" BIGINT, "discontinued" BOOLEAN))"); - -// res = s.drop().table("person").execute(); -// REQUIRE(res.first == 1); + REQUIRE(res.second == R"(DROP TABLE "person")"); } TEST_CASE("Execute create table statement with foreign keys", "[session]") { + // res = s.create().table("product").execute(); + // REQUIRE(res.second == R"(CREATE TABLE "product" ("product_name" VARCHAR(255) PRIMARY KEY, "supplier_id" BIGINT NOT NULL FOREIGN KEY, "category_id" BIGINT NOT NULL FOREIGN KEY, "quantity_per_unit" VARCHAR(255), "unit_price" BIGINT, "units_in_stock" BIGINT, "units_in_order" BIGINT, "reorder_level" BIGINT, "discontinued" BOOLEAN))"); + + // res = s.drop().table("person").execute(); + // REQUIRE(res.first == 1); } -TEST_CASE("Execute drop table statement", "[session]") { +TEST_CASE("Execute insert record statement", "[session]") { connection_pool pool("sqlite://sqlite.db", 4); session s(pool); - const auto res = s.drop() - .table("person") - .execute(); + auto res = s + .create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(); - REQUIRE(res.second == R"(DROP TABLE "person")"); + REQUIRE(res.first == 0); + + res = s + .insert() + .into("person", {"id", "name", "age"}) + .values({7, "george", 45}) + .execute(); + + REQUIRE(res.first == 1); + REQUIRE(res.second == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 45))"); + + auto result = s + .select({"id", "name", "age"}) + .from("person") + .fetch_all(); + + for (const auto& i : result) { + REQUIRE(i.size() == 3); + REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).type() == data_type_t::type_long_long); + REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).type() == data_type_t::type_varchar); + REQUIRE(i.at(2).name() == "age"); + REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int); + } + s.drop().table("person").execute(); + + // using namespace matador::test; + // product p; + // p.discontinued = false; + // p.reorder_level = 1; + // p.units_in_order = 2; + // p.units_in_stock = 100; + // p.unit_price = 49; + // p.quantity_per_unit = "pcs"; + // p.category = make_foreign(); + // p.category->id = 7; + // p.supplier = make_foreign();; + // p.supplier->id = 13; + // p.product_name = "candle"; + // + // res = s.insert().into("product").values(p).execute(); + // + // REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))"); + // + // res = s.insert().into("product", p).execute(); + // + // REQUIRE(res.second == R"(INSERT INTO "product" ("product_name", "supplier_id", "category_id", "quantity_per_unit", "unit_price", "units_in_stock", "units_in_order", "reorder_level", "discontinued") VALUES ('candle', 13, 7, 'pcs', 49, 100, 2, 1, 0))"); } TEST_CASE("Execute select statement with where clause", "[session]") { @@ -114,7 +165,7 @@ TEST_CASE("Execute insert statement", "[session]") { p.quantity_per_unit = "pcs"; p.category = make_foreign(); p.category->id = 7; - p.supplier = make_foreign();; + p.supplier = make_foreign(); p.supplier->id = 13; p.product_name = "candle"; @@ -151,7 +202,7 @@ TEST_CASE("Execute update statement", "[session]") { p.quantity_per_unit = "pcs"; p.category = make_foreign(); p.category->id = 7; - p.supplier = make_foreign();; + p.supplier = make_foreign(); p.supplier->id = 13; p.product_name = "candle";