column, placeholder and column_value generator progress

This commit is contained in:
Sascha Kühl 2025-11-11 16:19:58 +01:00
parent 1d84b112f1
commit 9e76203bc9
11 changed files with 227 additions and 54 deletions

View File

@ -6,28 +6,69 @@
#include <matador/utils/field_attributes.hpp> #include <matador/utils/field_attributes.hpp>
#include <matador/utils/foreign_attributes.hpp> #include <matador/utils/foreign_attributes.hpp>
#include "matador/object/repository.hpp"
#include "matador/query/fk_value_extractor.hpp" #include "matador/query/fk_value_extractor.hpp"
#include "matador/query/internal/column_value_pair.hpp" #include "matador/query/internal/column_value_pair.hpp"
#include "matador/sql/table.hpp"
#include <vector> #include <vector>
namespace matador::query::generator { namespace matador::query::generator {
class column_generator final { enum class column_generator_options {
None = 0,
GenerateAlias = 1 << 0,
ForceLazy = 1 << 1
};
template<typename EnumType>
class enum_flags {
public: public:
explicit enum_flags(EnumType value) : value_(value) {}
private:
EnumType value_;
};
inline column_generator_options operator|(column_generator_options a, column_generator_options b) { return static_cast<column_generator_options>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); }
inline column_generator_options operator&(column_generator_options a, column_generator_options b) { return static_cast<column_generator_options>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); }
inline column_generator_options& operator|= (column_generator_options& a, column_generator_options b) { return reinterpret_cast<column_generator_options&>(reinterpret_cast<int&>(a) |= static_cast<int>(b)); }
inline column_generator_options& operator&= (column_generator_options& a, column_generator_options b) { return reinterpret_cast<column_generator_options&>(reinterpret_cast<int&>(a) &= static_cast<int>(b)); }
inline bool is_column_generator_option_set(const column_generator_options source, const column_generator_options needle) { return static_cast<int>(source & needle) > 0; }
constexpr auto default_column_generator_options = column_generator_options::ForceLazy;
class column_generator2 {
public:
explicit column_generator2(const std::string &table_name = "",
column_generator_options options = default_column_generator_options);
explicit column_generator2(const object::repository &repo,
const std::string &table_name = "",
column_generator_options options = default_column_generator_options);
template< class Type >
std::vector<sql::column> generate() {
Type obj;
return generate(obj);
}
template< class Type >
std::vector<sql::column> generate(const Type &obj) {
result_.clear();
access::process(*this, obj);
return result_;
}
template < class V > template < class V >
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { 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); push(id);
} }
void on_revision(const char *id, uint64_t &/*rev*/); void on_revision(const char *id, uint64_t &/*rev*/);
template<typename Type> template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) { void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {
if (has_many_to_many_) {
return;
}
push(id); push(id);
} }
@ -42,32 +83,29 @@ public:
template<class ContainerType> template<class ContainerType>
void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) { void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) {
if (has_many_to_many_) { if (attr.fetch() == utils::fetch_type::LAZY || is_column_generator_option_set(options_, column_generator_options::ForceLazy)) {
return; return;
} }
if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) { if (!repo_) {
return; return;
} }
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>(); const auto info = repo_->get().info<typename ContainerType::value_type::value_type>();
if (!info) { if (!info) {
return; return;
} }
if (seen_tables.count(info->get().name()) == 0) { if (seen_tables.count(info->get().name()) == 0) {
auto it = seen_tables.insert(info->get().name()).first; auto it = seen_tables.insert(info->get().name()).first;
table_name_stack_.push(info.value().get().name()); table_stack_.push(std::make_shared<sql::table>(info.value().get().name()));
typename ContainerType::value_type::value_type obj; typename ContainerType::value_type::value_type obj;
access::process(*this, obj); access::process(*this, obj);
table_name_stack_.pop(); table_stack_.pop();
seen_tables.erase(it); seen_tables.erase(it);
} }
} }
template<class ContainerType> template<class ContainerType>
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*/) { 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(join_column);
push(inverse_join_column); push(inverse_join_column);
} }
@ -75,7 +113,41 @@ public:
template<class ContainerType> template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private:
template<class Pointer>
void on_foreign_key(const char *id, Pointer &, const utils::foreign_attributes &attr) {
if (attr.fetch() == utils::fetch_type::LAZY || is_column_generator_option_set(options_, column_generator_options::ForceLazy)) {
push(id);
} else {
if (!repo_) {
return;
}
const auto info = repo_->get().info<typename Pointer::value_type>();
if (!info) {
return;
}
if (seen_tables.count(info->get().name()) == 0) {
auto it = seen_tables.insert(info->get().name()).first;
table_stack_.push(std::make_shared<sql::table>(info.value().get().name()));
typename Pointer::value_type obj;
access::process(*this, obj);
table_stack_.pop();
seen_tables.erase(it);
}
}
}
void push(const std::string &column_name);
private:
std::optional<std::reference_wrapper<const object::repository>> repo_;
std::vector<sql::column> result_;
std::stack<std::shared_ptr<sql::table>> table_stack_;
std::unordered_set<std::string> seen_tables;
int column_index{0};
column_generator_options options_{false};
}; };
class placeholder_generator final { class placeholder_generator final {
public: public:
template< class Type > template< class Type >
@ -174,17 +246,43 @@ std::vector<utils::placeholder> placeholders(const Type &obj) {
return generator.generate(obj); return generator.generate(obj);
} }
template<typename Type>
std::vector<internal::column_value_pair> column_value_pairs() {
column_value_generator generator;
return generator.generate<Type>();
}
template<typename Type> template<typename Type>
std::vector<internal::column_value_pair> column_value_pairs(const Type &obj) { std::vector<internal::column_value_pair> column_value_pairs(const Type &obj) {
column_value_generator generator; column_value_generator generator;
return generator.generate(obj); return generator.generate(obj);
} }
template<typename Type>
std::vector<sql::column> columns(const object::repository &repo,
const std::string &table_name = "",
const column_generator_options options = default_column_generator_options) {
column_generator2 generator(repo, table_name, options);
return generator.generate<Type>();
}
template<typename Type>
std::vector<sql::column> columns(const Type &obj,
const object::repository &repo,
const std::string &table_name = "",
const column_generator_options options = default_column_generator_options) {
column_generator2 generator(repo, table_name, options);
return generator.generate(obj);
}
template<typename Type>
std::vector<sql::column> columns(const std::string &table_name = "",
const column_generator_options options = default_column_generator_options) {
column_generator2 generator(table_name, options);
return generator.generate<Type>();
}
template<typename Type>
std::vector<sql::column> columns(const Type &obj,
const std::string &table_name = "",
const column_generator_options options = default_column_generator_options) {
column_generator2 generator(table_name, options);
return generator.generate(obj);
}
} }
#endif //MATADOR_GENERATOR_HPP #endif //MATADOR_GENERATOR_HPP

View File

@ -15,11 +15,14 @@ public:
column_value_pair(const char *name, utils::database_type value); column_value_pair(const char *name, utils::database_type value);
column_value_pair(const char *name, utils::placeholder p); column_value_pair(const char *name, utils::placeholder p);
[[nodiscard]] const std::string& name() const; friend bool operator==(const column_value_pair &lhs, const column_value_pair &rhs);
friend bool operator!=(const column_value_pair &lhs, const column_value_pair &rhs);
[[nodiscard]] const sql::column& col() const;
[[nodiscard]] const std::variant<utils::placeholder, utils::database_type>& value() const; [[nodiscard]] const std::variant<utils::placeholder, utils::database_type>& value() const;
private: private:
std::string name_; sql::column column_;
std::variant<utils::placeholder, utils::database_type> value_; std::variant<utils::placeholder, utils::database_type> value_;
}; };

View File

@ -320,7 +320,7 @@ class query_set_part final : public query_part
public: public:
explicit query_set_part(const std::vector<internal::column_value_pair>& key_value_pairs); explicit query_set_part(const std::vector<internal::column_value_pair>& key_value_pairs);
[[nodiscard]] const std::vector<internal::column_value_pair>& key_values() const; [[nodiscard]] const std::vector<internal::column_value_pair>& column_values() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;

View File

@ -40,19 +40,6 @@ public:
static std::vector<column> generate(const object::repository &scm, const std::string &name, bool force_lazy = false); static std::vector<column> generate(const object::repository &scm, const std::string &name, bool force_lazy = false);
template < class Type >
static std::vector<column> generate_has_many_to_many(const object::repository &scm, const bool force_lazy = false) {
const auto info = scm.info<Type>();
if (!info) {
return {};
}
std::vector<column> columns;
column_generator gen(columns, scm, info.value().get().name(), force_lazy);
Type obj;
access::process(gen, obj);
return columns;
}
template < class V > template < class V >
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
push(id); push(id);

View File

@ -74,7 +74,7 @@ void session_query_builder::append_join(const sql::column &left, const sql::colu
using namespace matador::query; using namespace matador::query;
entity_query_data_.joins.push_back({ entity_query_data_.joins.push_back({
{right.table_}, {right.table_},
left == right std::make_unique<binary_column_criteria>(left, binary_operator::EQUALS, right)
}); });
} }
} }

View File

@ -1,6 +1,31 @@
#include "matador/query/generator.hpp" #include "matador/query/generator.hpp"
namespace matador::query::generator { namespace matador::query::generator {
column_generator2::column_generator2( const std::string& table_name, const column_generator_options options)
: options_(options) {
table_stack_.push(table_name.empty() ? std::make_shared<sql::table>() : std::make_shared<sql::table>(table_name));
}
column_generator2::column_generator2(const object::repository& repo, const std::string& table_name, const column_generator_options options)
: repo_(std::cref(repo))
, options_(options) {
table_stack_.push(table_name.empty() ? std::make_shared<sql::table>() : std::make_shared<sql::table>(table_name));
}
void column_generator2::on_revision( const char* id, uint64_t& ) {
push(id);
}
void column_generator2::push( const std::string& column_name ) {
if (is_column_generator_option_set(options_, column_generator_options::GenerateAlias)) {
char str[4];
snprintf(str, 4, "c%02d", ++column_index);
result_.emplace_back(table_stack_.top(), column_name, str);
} else {
result_.emplace_back(table_stack_.top(), column_name);
}
}
void placeholder_generator::on_revision(const char* /*id*/, uint64_t&) { void placeholder_generator::on_revision(const char* /*id*/, uint64_t&) {
result_.emplace_back(utils::_); result_.emplace_back(utils::_);
} }

View File

@ -5,29 +5,37 @@
namespace matador::query::internal { namespace matador::query::internal {
column_value_pair::column_value_pair(std::string name, utils::database_type value) column_value_pair::column_value_pair(std::string name, utils::database_type value)
: name_(std::move(name)) : column_(std::move(name))
, value_(std::move(value)) { , value_(std::move(value)) {
} }
column_value_pair::column_value_pair(const sql::column &col, utils::database_type value) column_value_pair::column_value_pair(const sql::column &col, utils::database_type value)
: name_(col.name) : column_(col)
, value_(std::move(value)) { , value_(std::move(value)) {
} }
column_value_pair::column_value_pair(const char *name, utils::database_type value) column_value_pair::column_value_pair(const char *name, utils::database_type value)
: name_(name) : column_(name)
, value_(std::move(value)) { , value_(std::move(value)) {
} }
column_value_pair::column_value_pair( const char* name, utils::placeholder p ) column_value_pair::column_value_pair( const char* name, utils::placeholder p )
: name_(name) : column_(name)
, value_(p) {} , value_(p) {}
const std::string &column_value_pair::name() const { const sql::column &column_value_pair::col() const {
return name_; return column_;
} }
const std::variant<utils::placeholder, utils::database_type>& column_value_pair::value() const { const std::variant<utils::placeholder, utils::database_type>& column_value_pair::value() const {
return value_; return value_;
} }
bool operator==( const column_value_pair& lhs, const column_value_pair& rhs ) {
return lhs.column_.equals(rhs.column_) && lhs.value_ == rhs.value_;
}
bool operator!=( const column_value_pair& lhs, const column_value_pair& rhs ) {
return !( lhs == rhs );
}
} }

View File

@ -304,7 +304,7 @@ query_set_part::query_set_part(const std::vector<column_value_pair>& key_value_p
: query_part(sql::dialect_token::Set) : query_part(sql::dialect_token::Set)
, key_value_pairs_(key_value_pairs) {} , key_value_pairs_(key_value_pairs) {}
const std::vector<column_value_pair> &query_set_part::key_values() const const std::vector<column_value_pair> &query_set_part::column_values() const
{ {
return key_value_pairs_; return key_value_pairs_;
} }

View File

@ -363,18 +363,18 @@ void query_compiler::visit(internal::query_set_part &part) {
attribute_string_writer writer(*dialect_, connection_); attribute_string_writer writer(*dialect_, connection_);
std::string result; std::string result;
value_visitor visitor(writer, query_); if (part.key_values().size() < 2) { value_visitor visitor(writer, query_); if (part.column_values().size() < 2) {
for (const auto &col: part.key_values()) { for (const auto &column_value: part.column_values()) {
result.append(dialect_->prepare_identifier_string(col.name()) + "="); result.append(dialect_->prepare_identifier_string(column_value.col().name) + "=");
result.append(determine_value(visitor, col.value())); result.append(determine_value(visitor, column_value.value()));
} }
} else { } else {
auto it = part.key_values().begin(); auto it = part.column_values().begin();
result.append(dialect_->prepare_identifier_string(it->name()) + "="); result.append(dialect_->prepare_identifier_string(it->col().name) + "=");
result.append(determine_value(visitor, (it++)->value())); result.append(determine_value(visitor, (it++)->value()));
for (; it != part.key_values().end(); ++it) { for (; it != part.column_values().end(); ++it) {
result.append(", "); result.append(", ");
result.append(dialect_->prepare_identifier_string(it->name()) + "="); result.append(dialect_->prepare_identifier_string(it->col().name) + "=");
result.append(determine_value(visitor, it->value())); result.append(determine_value(visitor, it->value()));
} }
} }

View File

@ -27,6 +27,7 @@ add_executable(OrmTests
sql/StatementCacheTest.cpp sql/StatementCacheTest.cpp
utils/auto_reset_event.cpp utils/auto_reset_event.cpp
utils/auto_reset_event.hpp utils/auto_reset_event.hpp
query/GeneratorTests.cpp
) )
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)

View File

@ -0,0 +1,51 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/query/generator.hpp"
#include "matador/sql/column.hpp"
#include "../../models/airplane.hpp"
#include "../../models/flight.hpp"
using namespace matador::utils;
using namespace matador::query;
using namespace matador::sql;
using namespace matador::test;
TEST_CASE("Test placeholder generator", "[generator][placeholder]") {
REQUIRE(generator::placeholders<airplane>() == std::vector{_, _, _});
}
TEST_CASE("Test column value generator", "[generator][column_value]") {
SECTION("Test column value generator for simple table") {
const airplane a380{1, "Airbus", "A380"};
const auto pairs = generator::column_value_pairs(a380);
const std::vector<internal::column_value_pair> expected_result{
{ "id", 1U },
{ "brand", std::string{"Airbus"} },
{ "model", std::string{"A380"} }
};
REQUIRE(pairs.size() == 3);
REQUIRE(pairs == expected_result);
}
SECTION("Test column value generator for table with foreign key") {
}
}
TEST_CASE("Test column generator", "[generator][column]") {
SECTION("Test column generator for simple table") {
const std::vector expected_columns {
column("id"),
column("brand"),
column("model"),
};
const auto result = generator::columns<airplane>();
REQUIRE(result.size() == expected_columns.size());
auto it = result.begin();
for (const auto& c : expected_columns) {
REQUIRE(c.equals( *it++));
}
}
}