From 0d08ad41797d98c9bed1851719c30afc943c2d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Wed, 12 Nov 2025 22:18:56 +0100 Subject: [PATCH] column generator progress --- include/matador/orm/session.hpp | 2 +- include/matador/query/generator.hpp | 124 +++++++++++------- .../intermediates/query_into_intermediate.hpp | 5 - .../query_update_intermediate.hpp | 5 - include/matador/sql/generator.hpp | 112 ++++++++++++++++ source/orm/query/generator.cpp | 5 +- test/backends/QueryBasicTest.cpp | 2 +- test/backends/QueryStatementTests.cpp | 10 +- test/backends/TypeTraitsTest.cpp | 2 +- 9 files changed, 202 insertions(+), 65 deletions(-) create mode 100644 include/matador/sql/generator.hpp diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index bbfe3a3..c7e1630 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -248,7 +248,7 @@ utils::result, utils::error> session::update( const obj const auto col = sql::column(info.value().get().definition().primary_key()->name()); auto res = matador::query::query::update(info->get().name()) - .set(generator::placeholders()) + .set(generator::column_value_pairs()) .where(col == _) .prepare(*this); if (!res) { diff --git a/include/matador/query/generator.hpp b/include/matador/query/generator.hpp index 5904072..8750a35 100644 --- a/include/matador/query/generator.hpp +++ b/include/matador/query/generator.hpp @@ -22,13 +22,18 @@ enum class column_generator_options { ForceLazy = 1 << 1 }; -template -class enum_flags { -public: - explicit enum_flags(EnumType value) : value_(value) {} -private: - EnumType value_; -}; +// template +// class enum_flags { +// public: +// explicit enum_flags(EnumType value) : value_(value) {} +// +// operator EnumType() const { return value_; } +// +// enum_flags operator|=(EnumType other) { value_ |= other; return *this; } +// enum_flags operator&=(EnumType other) { value_ |= other; return *this; }) +// private: +// EnumType value_; +// }; inline column_generator_options operator|(column_generator_options a, column_generator_options b) { return static_cast(static_cast(a) | static_cast(b)); } inline column_generator_options operator&(column_generator_options a, column_generator_options b) { return static_cast(static_cast(a) & static_cast(b)); } @@ -187,7 +192,7 @@ public: result_.emplace_back(utils::_); } template - static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) {} + static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} template static void on_has_many_to_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} template @@ -196,53 +201,74 @@ public: private: std::vector result_; }; +enum class column_value_generator_options { + None = 0, + ValuesAsPlaceHolder = 1 << 0 +}; + +inline column_value_generator_options operator|(column_value_generator_options a, column_value_generator_options b) { return static_cast(static_cast(a) | static_cast(b)); } +inline column_value_generator_options operator&(column_value_generator_options a, column_value_generator_options b) { return static_cast(static_cast(a) & static_cast(b)); } +inline column_value_generator_options& operator|= (column_value_generator_options& a, column_value_generator_options b) { return reinterpret_cast(reinterpret_cast(a) |= static_cast(b)); } +inline column_value_generator_options& operator&= (column_value_generator_options& a, column_value_generator_options b) { return reinterpret_cast(reinterpret_cast(a) &= static_cast(b)); } + +inline bool is_column_value_generator_option_set(const column_value_generator_options source, const column_value_generator_options needle) { return static_cast(source & needle) > 0; } class column_value_generator final { public: - template< class Type > - std::vector generate() { - Type obj; - return generate(obj); - } + explicit column_value_generator(column_value_generator_options options = column_value_generator_options::None); + template< class Type > + std::vector generate() { + Type obj; + return generate(obj); + } - template< class Type > - std::vector generate(const Type &obj) { - result_.clear(); - access::process(*this, obj); + template< class Type > + std::vector generate(const Type &obj) { + result_.clear(); + access::process(*this, obj); - return result_; - } + return result_; + } - template < class V > - void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { - result_.emplace_back(id, x); - } - void on_revision(const char *id, uint64_t &/*rev*/); + template < class V > + void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { + push_back(id, x); + } + void on_revision(const char *id, uint64_t &/*rev*/); - template - void on_attribute(const char *id, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) { - result_.emplace_back(id, x); - } + template + void on_attribute(const char *id, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) { + push_back(id, x); + } - template class Pointer> - void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { - result_.emplace_back(id, fk_value_extractor_.extract(*x)); + template class Pointer> + void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { + push_back(id, fk_value_extractor_.extract(*x)); + } + template class Pointer> + void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { + push_back(id, fk_value_extractor_.extract(*x)); + } + template + static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} + template + static void on_has_many_to_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} + template + static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} + + template + void push_back(const char* id, const Type& value) { + if (is_column_value_generator_option_set(options_, column_value_generator_options::ValuesAsPlaceHolder)) { + result_.emplace_back(id, utils::_); + } else { + result_.emplace_back(id, value); } - template class Pointer> - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) { - result_.emplace_back(id, fk_value_extractor_.extract(*x)); - } - template - static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) {} - template - static void on_has_many_to_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} - template - static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} + } private: - detail::fk_value_extractor fk_value_extractor_{}; - std::vector result_; - + detail::fk_value_extractor fk_value_extractor_{}; + std::vector result_; + column_value_generator_options options_{column_value_generator_options::None}; }; template @@ -258,11 +284,17 @@ std::vector placeholders(const Type &obj) { } template -std::vector column_value_pairs(const Type &obj) { - column_value_generator generator; +std::vector column_value_pairs(const Type &obj, const column_value_generator_options options = column_value_generator_options::None) { + column_value_generator generator(options); return generator.generate(obj); } +template +std::vector column_value_pairs() { + Type obj; + return column_value_pairs(obj, column_value_generator_options::ValuesAsPlaceHolder); +} + template std::vector columns(const object::repository &repo, const std::string &table_name = "", diff --git a/include/matador/query/intermediates/query_into_intermediate.hpp b/include/matador/query/intermediates/query_into_intermediate.hpp index d298d83..c0e68fb 100644 --- a/include/matador/query/intermediates/query_into_intermediate.hpp +++ b/include/matador/query/intermediates/query_into_intermediate.hpp @@ -19,11 +19,6 @@ public: executable_query values(std::vector &&values); executable_query values(std::vector &&values); template - executable_query values() { - Type obj; - return values(obj); - } - template executable_query values(const Type &obj) { return values(value_extractor::extract(obj)); } diff --git a/include/matador/query/intermediates/query_update_intermediate.hpp b/include/matador/query/intermediates/query_update_intermediate.hpp index d09ea93..3412b0c 100644 --- a/include/matador/query/intermediates/query_update_intermediate.hpp +++ b/include/matador/query/intermediates/query_update_intermediate.hpp @@ -18,11 +18,6 @@ public: query_set_intermediate set(std::initializer_list columns); query_set_intermediate set(std::vector &&columns); template - query_set_intermediate set() { - Type obj; - return set(obj); - } - template query_set_intermediate set(const Type &obj) { return set(key_value_generator::generate(obj)); } diff --git a/include/matador/sql/generator.hpp b/include/matador/sql/generator.hpp new file mode 100644 index 0000000..316cedc --- /dev/null +++ b/include/matador/sql/generator.hpp @@ -0,0 +1,112 @@ +#ifndef MATADOR_GENERATOR_HPP +#define MATADOR_GENERATOR_HPP + +#include + +#include + +#include + +namespace matador::sql::generator { +template < class Type > +static std::vector placeholder(const object::repository &repo); +template < class Type > +static std::vector columns(const object::repository &repo); +template < class Type > +static std::vector key_values(const object::repository &repo); + +class placeholder_generator { +public: + + template < class Type > + static std::vector generate(const object::repository &repo, const bool force_lazy = false) { + const auto info = repo.info(); + if (!info) { + return {}; + } + std::vector columns; + placeholder_generator gen(columns, repo, info.value().get().name(), force_lazy); + Type obj; + access::process(gen, obj); + return columns; + } + + template < class Type > + static std::vector generate_has_many_to_many(const object::repository &scm, const bool force_lazy = false) { + const auto info = scm.info(); + if (!info) { + return {}; + } + std::vector columns; + placeholder_generator gen(columns, scm, info.value().get().name(), force_lazy); + Type obj; + access::process(gen, obj); + return columns; + } + + template < class V > + void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { + if (has_many_to_many_) { + return; + } + push(id); + } + void on_revision(const char *id, uint64_t &/*rev*/); + + template + void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) { + if (has_many_to_many_) { + return; + } + push(id); + } + + template + void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &attr) { + on_foreign_key(id, x, attr); + } + template + void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &attr) { + on_foreign_key(id, x, attr); + } + + template + void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) { + if (has_many_to_many_) { + return; + } + if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) { + return; + } + const auto info = table_schema_.info(); + if (!info) { + return; + } + + if (seen_tables.count(info->get().name()) == 0) { + auto it = seen_tables.insert(info->get().name()).first; + table_name_stack_.push(info.value().get().name()); + typename ContainerType::value_type::value_type obj; + access::process(*this, obj); + table_name_stack_.pop(); + seen_tables.erase(it); + } + } + + template + void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) { + if (!has_many_to_many_) { + return; + } + push(join_column); + push(inverse_join_column); + } + + template + static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {} + +private: + +}; +} +#endif //MATADOR_GENERATOR_HPP \ No newline at end of file diff --git a/source/orm/query/generator.cpp b/source/orm/query/generator.cpp index a962197..e130579 100644 --- a/source/orm/query/generator.cpp +++ b/source/orm/query/generator.cpp @@ -30,7 +30,10 @@ void placeholder_generator::on_revision(const char* /*id*/, uint64_t&) { result_.emplace_back(utils::_); } +column_value_generator::column_value_generator(column_value_generator_options options) +: options_(options) {} + void column_value_generator::on_revision(const char* id, uint64_t& x) { - result_.emplace_back(id, x); + push_back(id, x); } } \ No newline at end of file diff --git a/test/backends/QueryBasicTest.cpp b/test/backends/QueryBasicTest.cpp index 3ef3fd0..52b6769 100644 --- a/test/backends/QueryBasicTest.cpp +++ b/test/backends/QueryBasicTest.cpp @@ -393,7 +393,7 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key auto stmt = query::insert() .into("pk", generator::columns(repo)) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); diff --git a/test/backends/QueryStatementTests.cpp b/test/backends/QueryStatementTests.cpp index a384fdc..24eb3e8 100644 --- a/test/backends/QueryStatementTests.cpp +++ b/test/backends/QueryStatementTests.cpp @@ -60,7 +60,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][inse stmt = query::insert() .into("person", repo) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); @@ -102,7 +102,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda stmt = query::insert() .into("person", repo) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); @@ -125,7 +125,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda george.age = 36; george.image = {5,6,7,8}; stmt = query::update("person") - .set() + .set(generator::column_value_pairs()) .where("id"_col == _) .prepare(db); REQUIRE(stmt); @@ -166,7 +166,7 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele stmt = query::insert() .into("person", repo) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); @@ -253,7 +253,7 @@ TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][stateme stmt = query::insert() .into("person", repo) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); diff --git a/test/backends/TypeTraitsTest.cpp b/test/backends/TypeTraitsTest.cpp index 8a6f16e..e9ab25e 100644 --- a/test/backends/TypeTraitsTest.cpp +++ b/test/backends/TypeTraitsTest.cpp @@ -56,7 +56,7 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ auto stmt = query::insert() .into("location", generator::columns(repo)) - .values() + .values(generator::placeholders()) .prepare(db); REQUIRE(stmt); auto res = stmt->bind(loc)