From 980cabe94f92fd40d85e340f93e1fb8556f60cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 4 Dec 2025 16:06:39 +0100 Subject: [PATCH] fixed schema creation --- backends/postgres/src/postgres_connection.cpp | 8 +- include/matador/object/basic_object_info.hpp | 2 + include/matador/orm/schema.hpp | 6 ++ .../query_alter_table_intermediate.hpp | 4 +- include/matador/sql/query_context.hpp | 12 +-- source/core/object/basic_object_info.cpp | 4 + source/core/object/object.cpp | 7 ++ source/core/object/repository_node.cpp | 4 +- source/orm/orm/schema.cpp | 101 +++++++++++------- source/orm/orm/session.cpp | 6 +- .../query_alter_table_intermediate.cpp | 4 + source/orm/query/query_compiler.cpp | 89 +++++++-------- 12 files changed, 152 insertions(+), 95 deletions(-) diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index fbccc92..8870dc1 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -111,10 +111,10 @@ utils::result, utils::error> postgres_co std::string postgres_connection::generate_statement_name(const sql::query_context &query) { std::stringstream name; name << query.table_name << "_" << query.command_name; - auto result = postgres_connection::statement_name_map_.find(name.str()); + auto result = statement_name_map_.find(name.str()); - if (result == postgres_connection::statement_name_map_.end()) { - result = postgres_connection::statement_name_map_.insert(std::make_pair(name.str(), 0)).first; + if (result == statement_name_map_.end()) { + result = statement_name_map_.insert(std::make_pair(name.str(), 0)).first; } name << "_" << ++result->second; @@ -123,7 +123,7 @@ std::string postgres_connection::generate_statement_name(const sql::query_contex } utils::result, utils::error> postgres_connection::prepare(const sql::query_context &context) { - auto statement_name = postgres_connection::generate_statement_name(context); + auto statement_name = generate_statement_name(context); const PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast(context.bind_vars.size()), nullptr); diff --git a/include/matador/object/basic_object_info.hpp b/include/matador/object/basic_object_info.hpp index 8aace6d..4f375dd 100644 --- a/include/matador/object/basic_object_info.hpp +++ b/include/matador/object/basic_object_info.hpp @@ -33,6 +33,8 @@ public: [[nodiscard]] const utils::identifier& primary_key() const; [[nodiscard]] attribute* primary_key_attribute() const; + void update_name(const std::string& name); + endpoint_iterator register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint); void unregister_relation_endpoint(const std::type_index &type); diff --git a/include/matador/orm/schema.hpp b/include/matador/orm/schema.hpp index 411960b..15eb1cb 100644 --- a/include/matador/orm/schema.hpp +++ b/include/matador/orm/schema.hpp @@ -3,6 +3,8 @@ #include "matador/object/repository.hpp" +#include "matador/sql/query_context.hpp" + namespace matador::sql { class connection_pool; } @@ -49,6 +51,10 @@ public: [[nodiscard]] utils::result, utils::error> describe_table(const std::string &table_name) const; [[nodiscard]] utils::result table_exists(const std::string &table_name) const; +private: + sql::query_context build_add_constraint_context( const object::repository_node& node, const class object::constraint& cons ) const; + sql::query_context build_drop_constraint_context( const object::repository_node& node, const class object::constraint& cons ) const; + private: object::repository repo_; sql::connection_pool &pool_; diff --git a/include/matador/query/intermediates/query_alter_table_intermediate.hpp b/include/matador/query/intermediates/query_alter_table_intermediate.hpp index 6f6b436..e515f1d 100644 --- a/include/matador/query/intermediates/query_alter_table_intermediate.hpp +++ b/include/matador/query/intermediates/query_alter_table_intermediate.hpp @@ -2,10 +2,11 @@ #define MATADOR_QUERY_ALTER_TABLE_INTERMEDIATE_HPP #include "executable_query.hpp" +#include "matador/object/constraint.hpp" #include "matador/query/intermediates/query_intermediate.hpp" namespace matador::query { -class query_add_primary_key_constraint_intermediate final : public query_intermediate { +class query_add_primary_key_constraint_intermediate final : public executable_query { public: }; @@ -35,6 +36,7 @@ public: using query_intermediate::query_intermediate; query_add_key_constraint_intermediate add_constraint(const std::string& name); + executable_query add_constraint(const class object::constraint& constraint); executable_query drop_constraint(const std::string& name); }; } diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index 3252fb4..844eafa 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -23,7 +23,9 @@ enum class sql_command { SQL_DROP_TABLE, SQL_DROP_SCHEMA, SQL_DROP_DATABASE, - SQL_ALTER_TABLE + SQL_ALTER, + SQL_ALTER_TABLE, + SQL_ALTER_SCHEMA }; struct sql_command_info { @@ -38,14 +40,12 @@ struct query_context { std::string schema_name{}; std::string table_name{}; std::vector prototype{}; - std::vector result_vars{}; + // std::vector result_vars{}; std::vector bind_vars{}; std::vector bind_types{}; - std::unordered_map column_aliases{}; - std::unordered_map table_aliases{}; - - std::vector additional_commands{}; + // std::unordered_map column_aliases{}; + // std::unordered_map table_aliases{}; }; } diff --git a/source/core/object/basic_object_info.cpp b/source/core/object/basic_object_info.cpp index ffaa0e7..f5b7808 100644 --- a/source/core/object/basic_object_info.cpp +++ b/source/core/object/basic_object_info.cpp @@ -52,6 +52,10 @@ attribute* basic_object_info::primary_key_attribute() const { return object_->primary_key_attribute(); } +void basic_object_info::update_name(const std::string& name) { + object_->update_name(name); +} + basic_object_info::endpoint_iterator basic_object_info::register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint) { return relation_endpoints_.insert(std::make_pair(type, endpoint)).first; } diff --git a/source/core/object/object.cpp b/source/core/object/object.cpp index 6e1c8d0..4cd2a4d 100644 --- a/source/core/object/object.cpp +++ b/source/core/object/object.cpp @@ -43,6 +43,13 @@ const std::string& object::alias() const { void object::update_name(const std::string& name) { name_ = name; + for (auto& con : constraints_) { + if (con.is_primary_key_constraint()) { + con.name_ += name; + } else if (con.is_foreign_key_constraint()) { + con.name_ = "FK_" + name + "_" + con.column_name(); + } + } } bool object::has_attributes() const { diff --git a/source/core/object/repository_node.cpp b/source/core/object/repository_node.cpp index 3e252d0..7188e18 100644 --- a/source/core/object/repository_node.cpp +++ b/source/core/object/repository_node.cpp @@ -45,9 +45,7 @@ const basic_object_info &repository_node::info() const { void repository_node::update_name(const std::string& name) { name_ = name; - // if (info_->reference_column()) { - // info_->reference_column()->table_name(name); - // } + info_->update_name(name); } const repository& repository_node::schema() const { diff --git a/source/orm/orm/schema.cpp b/source/orm/orm/schema.cpp index 51506b5..2722ff6 100644 --- a/source/orm/orm/schema.cpp +++ b/source/orm/orm/schema.cpp @@ -18,9 +18,8 @@ schema::schema(sql::connection_pool& pool) schema::schema(sql::connection_pool &pool, const std::string& name) : repo_(name) , pool_(pool) {} -} -matador::utils::result matador::orm::schema::create() const { +utils::result schema::create() const { // Step 1: Build dependency graph // std::unordered_map > dependency_graph; // std::unordered_map> in_degree; @@ -51,37 +50,40 @@ matador::utils::result matador::orm::schema::create // } std::vector fk_sql_commands; - auto c = pool_.acquire(); + const auto c = pool_.acquire(); - // auto result = query::query::create().schema( repo_.name() ).compile( *c ); - // std::cout << result.sql << std::endl; + const auto q = query::query::create().schema( repo_.name() ).compile( *c ); + std::cout << q.sql << std::endl; + + // create plain tables without constraints for (const auto &node: repo_) { auto ctx = query::query::create() .table(node->name()) .columns(node->info().attributes()) - .constraints(node->info().constraints()) + // .constraints(node->info().constraints()) .compile(*c); - for ( const auto& [sql, command] : ctx.additional_commands ) { - fk_sql_commands.push_back( sql ); - } std::cout << ctx.sql << std::endl; if (auto result = c->execute(ctx.sql); !result) { return utils::failure(result.err()); } } - // execute additional commands (e.g. ALTER TABLE ADD FK) - for (const auto &sql: fk_sql_commands) { - std::cout << sql << std::endl; - if (auto result = c->execute(sql); !result) { - return utils::failure(result.err()); + // create table constraints + for (const auto &node: repo_) { + for (const auto& cons : node->info().constraints()) { + auto ctx = build_add_constraint_context(*node, cons); + + std::cout << ctx.sql << std::endl; + if (auto result = c->execute(ctx.sql); !result) { + return utils::failure(result.err()); + } } } return utils::ok(); } -matador::utils::result matador::orm::schema::drop() const { +utils::result schema::drop() const { std::vector drop_sql_commands; auto c = pool_.acquire(); for (const auto &node: repo_) { @@ -117,30 +119,57 @@ matador::utils::result matador::orm::schema::drop() return utils::ok(); } -matador::utils::result matador::orm::schema::drop_table(const std::string& table_name) const { - const auto c = pool_.acquire(); - auto result = query::query::drop() - .table(table_name) - .execute(*c); - if (result.is_error()) { - return utils::failure(result.err()); - } +utils::result schema::drop_table(const std::string& table_name) const { + const auto c = pool_.acquire(); + auto result = query::query::drop() + .table(table_name) + .execute(*c); + if (result.is_error()) { + return utils::failure(result.err()); + } - return utils::ok(); + return utils::ok(); } -matador::utils::result, matador::utils::error> matador::orm::schema::describe_table(const std::string& table_name) const { - const auto c = pool_.acquire(); - if (!c.valid()) { - return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); - } - return utils::ok(c->describe(table_name).release()); +utils::result, utils::error> schema::describe_table(const std::string& table_name) const { + const auto c = pool_.acquire(); + if (!c.valid()) { + return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); + } + return utils::ok(c->describe(table_name).release()); } -matador::utils::result matador::orm::schema::table_exists(const std::string& table_name) const { - const auto c = pool_.acquire(); - if (!c.valid()) { - return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); - } - return c->exists(repo_.name(), table_name); +utils::result schema::table_exists(const std::string& table_name) const { + const auto c = pool_.acquire(); + if (!c.valid()) { + return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); + } + return c->exists(repo_.name(), table_name); } + +sql::query_context schema::build_add_constraint_context( const object::repository_node& node, const class object::constraint& cons ) const { + if (cons.is_foreign_key_constraint()) { + return query::query::alter() + .table(node.name()) + .add_constraint( cons.name() ) + .foreign_key(cons.column_name()) + .references(cons.ref_table_name(), {cons.ref_column_name()}) + .compile(*pool_.acquire()); + } + if (cons.is_primary_key_constraint()) { + return query::query::alter() + .table(node.name()) + .add_constraint( cons.name() ) + .primary_key(cons.column_name()) + .compile(*pool_.acquire()); + } + return {}; +} + +sql::query_context schema::build_drop_constraint_context( const object::repository_node& node, const class object::constraint& cons ) const { + return query::query::alter() + .table(node.name()) + .drop_constraint(cons.name()) + .compile(*pool_.acquire()); +} +} \ No newline at end of file diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index 4f436cf..eafd836 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -57,9 +57,9 @@ utils::result session::create_schema() const { .constraints(node->info().constraints()) .compile(*c); - for ( const auto& [sql, command] : ctx.additional_commands ) { - fk_sql_commands.push_back( sql ); - } + // for ( const auto& [sql, command] : ctx.additional_commands ) { + // fk_sql_commands.push_back( sql ); + // } std::cout << ctx.sql << std::endl; if (auto result = c->execute(ctx.sql); !result) { return utils::failure(result.err()); diff --git a/source/orm/query/intermediates/query_alter_table_intermediate.cpp b/source/orm/query/intermediates/query_alter_table_intermediate.cpp index 7d4c0c9..1222b02 100644 --- a/source/orm/query/intermediates/query_alter_table_intermediate.cpp +++ b/source/orm/query/intermediates/query_alter_table_intermediate.cpp @@ -35,6 +35,10 @@ query_add_key_constraint_intermediate query_alter_table_intermediate::add_constr return {context_}; } +executable_query query_alter_table_intermediate::add_constraint( const class object::constraint& constraint ) { + return {context_}; +} + executable_query query_alter_table_intermediate::drop_constraint( const std::string& name ) { context_->parts.push_back(std::make_unique(name)); return {context_}; diff --git a/source/orm/query/query_compiler.cpp b/source/orm/query/query_compiler.cpp index cd2d42e..45a253a 100644 --- a/source/orm/query/query_compiler.cpp +++ b/source/orm/query/query_compiler.cpp @@ -36,9 +36,9 @@ sql::query_context query_compiler::compile(const query_data &data, } std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const query_data &data, const column &col) { - ctx.result_vars.emplace_back(col.column_name()); + // ctx.result_vars.emplace_back(col.column_name()); const auto& column_table = col.table().get(); - ctx.column_aliases.insert({column_table->has_alias() ? column_table->alias() : column_table->name() + "." + col.column_name(), col.alias()}); + // ctx.column_aliases.insert({column_table->has_alias() ? column_table->alias() : column_table->name() + "." + col.column_name(), col.alias()}); if (col.is_function()) { ctx.prototype.emplace_back(col.has_alias() ? col.alias() : col.column_name()); ctx.prototype.back().change_type(utils::basic_type::type_int32); @@ -64,7 +64,7 @@ void query_compiler::visit(internal::query_alter_table_part& part) { } void query_compiler::visit(internal::query_add_key_constraint_part& part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::AddConstraint) + " " + part.name(); + query_.sql += " " + dialect_->add_constraint() + " " + part.name(); } void query_compiler::visit(internal::query_add_foreign_key_constraint_part& part) { @@ -84,7 +84,22 @@ void query_compiler::visit(internal::query_add_foreign_key_constraint_part& part query_.sql += ")"; } -void query_compiler::visit(internal::query_add_primary_key_constraint_part& part) {} +void query_compiler::visit(internal::query_add_primary_key_constraint_part& part) { + query_.sql += " " + dialect_->primary_key() + " ("; + + if (part.columns().size() < 2) { + for (const auto &col: part.columns()) { + query_.sql += dialect_->prepare_identifier_string(col.column_name()); + } + } else { + auto it = part.columns().begin(); + query_.sql += dialect_->prepare_identifier_string(it->column_name()); + for (; it != part.columns().end(); ++it) { + query_.sql += ", " + dialect_->prepare_identifier_string(it->column_name()); + } + } + query_.sql += ")"; +} void query_compiler::visit(internal::query_add_foreign_key_reference_part& part) { query_.sql += " " + dialect_->token_at(part.token()) + " " + part.table().name() + " ("; @@ -110,16 +125,15 @@ void query_compiler::visit(internal::query_drop_key_constraint_part& part) { void query_compiler::visit(internal::query_drop_foreign_key_constraint_part& part) { } -void query_compiler::visit(internal::query_select_part &part) -{ +void query_compiler::visit(internal::query_select_part &part) { query_.command = sql::sql_command::SQL_SELECT; - query_.sql = dialect_->token_at(sql::dialect_token::Select) + " "; + query_.sql = dialect_->select() + " "; query_.prototype.clear(); std::string result; if (part.columns().empty()) { - result = dialect_->token_at(sql::dialect_token::Asterisk); + result = dialect_->asterisk(); } else if (const auto &columns = part.columns(); columns.size() < 2) { for (const auto &col: columns) { result.append(handle_column(query_, dialect_, *data_, col )); @@ -139,7 +153,7 @@ void query_compiler::visit(internal::query_select_part &part) void query_compiler::visit(internal::query_from_part &part) { query_.table_name = part.table().name(); query_.sql += " " + build_table_name(part.token(), *dialect_, part.table()); - query_.table_aliases.insert({query_.table_name, part.table().alias()}); + // query_.table_aliases.insert({query_.table_name, part.table().alias()}); } void query_compiler::visit(internal::query_join_part &part) @@ -149,56 +163,56 @@ void query_compiler::visit(internal::query_join_part &part) void query_compiler::visit(internal::query_on_part &part) { criteria_evaluator evaluator(*dialect_, query_); - query_.sql += " " + dialect_->token_at(sql::dialect_token::On) + + query_.sql += " " + dialect_->on() + " " + evaluator.evaluate(part.condition()); } void query_compiler::visit(internal::query_where_part &part) { criteria_evaluator evaluator(*dialect_, query_); - query_.sql += " " + dialect_->token_at(sql::dialect_token::Where) + + query_.sql += " " + dialect_->where() + " " + evaluator.evaluate(part.condition()); } void query_compiler::visit(internal::query_group_by_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::GroupBy) + " " + prepare_identifier(*dialect_, part.column()); + query_.sql += " " + dialect_->group_by() + " " + prepare_identifier(*dialect_, part.column()); } void query_compiler::visit(internal::query_order_by_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::OrderBy) + + query_.sql += " " + dialect_->order_by() + " " + prepare_criteria(*dialect_, part.column()); } void query_compiler::visit(internal::query_order_by_asc_part &/*order_by_asc_part*/) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Asc); + query_.sql += " " + dialect_->asc(); } void query_compiler::visit(internal::query_order_by_desc_part &/*order_by_desc_part*/) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Desc); + query_.sql += " " + dialect_->desc(); } void query_compiler::visit(internal::query_offset_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Offset) + " " + std::to_string(part.offset()); + query_.sql += " " + dialect_->offset() + " " + std::to_string(part.offset()); } void query_compiler::visit(internal::query_limit_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Limit) + " " + std::to_string(part.limit()); + query_.sql += " " + dialect_->limit() + " " + std::to_string(part.limit()); } void query_compiler::visit(internal::query_insert_part &/*insert_part*/) { query_.command = sql::sql_command::SQL_INSERT; - query_.sql = dialect_->token_at(sql::dialect_token::Insert); + query_.sql = dialect_->insert(); } void query_compiler::visit(internal::query_into_part &part) { query_.table_name = part.table().name(); - query_.sql += " " + dialect_->token_at(sql::dialect_token::Into) + + query_.sql += " " + dialect_->into() + " " + dialect_->prepare_identifier_string(part.table().name()); std::string result{"("}; @@ -240,7 +254,7 @@ std::string query_compiler::determine_value(value_visitor &visitor, const std::v } void query_compiler::visit(internal::query_values_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Values); + query_.sql += " " + dialect_->values(); attribute_string_writer writer(*dialect_, connection_); @@ -275,7 +289,7 @@ void query_compiler::visit(internal::query_update_part &part) void query_compiler::visit(internal::query_delete_part &/*delete_part*/) { query_.command = sql::sql_command::SQL_DELETE; - query_.sql = dialect_->token_at(sql::dialect_token::Remove); + query_.sql = dialect_->remove(); } void query_compiler::visit(internal::query_delete_from_part &part) @@ -287,7 +301,7 @@ void query_compiler::visit(internal::query_delete_from_part &part) void query_compiler::visit(internal::query_create_part &/*create_part*/) { query_.command = sql::sql_command::SQL_CREATE_TABLE; - query_.sql = dialect_->token_at(sql::dialect_token::Create); + query_.sql = dialect_->create(); } std::string build_create_column(const object::attribute &col, const sql::dialect &d); @@ -295,22 +309,9 @@ std::string build_constraint(const class object::constraint &cons, const sql::di void query_compiler::visit(internal::query_create_table_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Table) + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; + query_.sql += " " + dialect_->table() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; query_.table_name = part.table().name(); - // if (!context.primary_keys.empty()) { - // result.append(", CONSTRAINT PK_" + part.table().name() + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")"); - // } - // for (const auto &[column, reference_column]: context.foreign_contexts) { - // // ALTER TABLE Orders ADD CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID); - // std::string fk_cmd = "ALTER TABLE " + dialect_->prepare_identifier_string(query_.table_name) + " ADD"; - // fk_cmd += " CONSTRAINT FK_" + query_.table_name; - // fk_cmd += "_" + column; - // fk_cmd += " FOREIGN KEY (" + dialect_->prepare_identifier_string(column) + ")"; - // fk_cmd += " REFERENCES " + reference_column->table_name() + "(" + reference_column->name() + ")"; - // query_.additional_commands.push_back({fk_cmd, sql::sql_command::SQL_ALTER_TABLE}); - // } - finisher_ = [](sql::query_context &ctx) { ctx.sql += ")"; }; } @@ -343,8 +344,8 @@ void query_compiler::visit(internal::query_create_table_constraints_part& part) } void query_compiler::visit( internal::query_create_schema_part& part ) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Create) + " " + - dialect_->token_at(sql::dialect_token::Schema) + " " + dialect_->prepare_identifier_string(part.schema()); + query_.sql += " " + dialect_->create() + " " + + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); } void query_compiler::visit(internal::query_drop_part &part) { @@ -353,12 +354,12 @@ void query_compiler::visit(internal::query_drop_part &part) { } void query_compiler::visit( internal::query_drop_schema_part& part ) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Drop) + " " + - dialect_->token_at(sql::dialect_token::Schema) + " " + dialect_->prepare_identifier_string(part.schema()); + query_.sql += " " + dialect_->drop() + " " + + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); } void query_compiler::visit(internal::query_set_part &part) { - query_.sql += " " + dialect_->token_at(sql::dialect_token::Set) + " "; + query_.sql += " " + dialect_->set() + " "; attribute_string_writer writer(*dialect_, connection_); std::string result; @@ -410,7 +411,11 @@ std::string build_constraint(const class object::constraint& cons, const sql::di result.append(d.constraint()).append(" ").append(cons.name()).append(" "); } if (cons.is_primary_key_constraint()) { - result.append(d.primary_key()).append(" (").append(cons.column_name()).append(")"); + result + .append(d.primary_key()) + .append(" (") + .append(cons.column_name()) + .append(")"); } else if (cons.is_foreign_key_constraint()) { result .append(d.foreign_key())