ensure proper table creation in session::create_schema including foreign key constraints

This commit is contained in:
Sascha Kühl
2025-07-17 15:56:15 +02:00
parent 581b93c5ea
commit fa393a1e30
16 changed files with 186 additions and 221 deletions
-1
View File
@@ -4,7 +4,6 @@ add_library(matador-orm STATIC
../../include/matador/query/condition.hpp
../../include/matador/query/fk_value_extractor.hpp
../../include/matador/query/intermediates/executable_query.hpp
../../include/matador/query/intermediates/executable_query.hpp
../../include/matador/query/intermediates/fetchable_query.hpp
../../include/matador/query/intermediates/query_create_intermediate.hpp
../../include/matador/query/intermediates/query_delete_from_intermediate.hpp
+18 -60
View File
@@ -25,12 +25,8 @@ utils::result<void, utils::error> session::create_schema() const {
for (const auto &node: *schema_) {
for (auto it = node->info().endpoint_begin(); it != node->info().endpoint_end(); ++it) {
std::cout << "Dependency graph " << node->name() << " (" << node.get() << ")" << " -> " << it->second->node_ptr()->name() << " (" << it->second->node_ptr().get() << ")" << std::endl;
dependency_graph[node->name()].push_back(it->second->node().name());
// if (it->second->is_has_many()) {
// continue;
// }
if (const auto dit = in_degree.find(it->second->node().name()); dit == in_degree.end()) {
in_degree[it->second->node().name()] = std::make_pair(1, it->second->node_ptr());
} else {
@@ -51,67 +47,29 @@ utils::result<void, utils::error> session::create_schema() const {
}
std::cout << std::endl;
}
// Step 2: Perform topological sorting (Kahn's Algorithm)
std::queue<object::schema::node_ptr> zero_in_degree;
std::vector<object::schema::node_ptr> sorted_order;
for (const auto &[table, degree]: in_degree) {
if (degree.first == 0) {
zero_in_degree.push(degree.second);
}
}
for (const auto &it : in_degree) {
std::cout << "In degree table " << it.second.second->name() << " (" << it.second.first << ")" << std::endl;
}
while (!zero_in_degree.empty()) {
auto current = zero_in_degree.front();
zero_in_degree.pop();
sorted_order.push_back(current);
for (const auto &neighbor: dependency_graph[current->name()]) {
in_degree[neighbor].first--;
if (in_degree[neighbor].first == 0) {
zero_in_degree.push(in_degree[neighbor].second);
}
}
}
// Step 3: Check for cycles
if (sorted_order.size() != in_degree.size()) {
std::cout << "Cycle detected in table dependencies (sorted order size: " << sorted_order.size() << ", in degree size: " << in_degree.size() << ")" << std::endl;
// throw std::logic_error("Cycle detected in table dependencies");
}
// Step 4: Create tables in the sorted order
std::vector<std::string> fk_sql_commands;
auto c = pool_.acquire();
for (const auto &node : sorted_order) {
std::cout << "Creating table " << node->name() << std::endl;
// auto result = query::query::create()
// .table(node->name(), node->info().definition().columns())
// .execute(*c);
// if (!result) {
// return utils::failure(result.err());
// }
for (const auto &node: *schema_) {
auto ctx = query::query::create()
.table(node->name(), node->info().definition().columns())
.compile(*c);
for ( const auto& [sql, command] : ctx.additional_commands ) {
fk_sql_commands.push_back( sql );
}
if (auto result = c->execute(ctx.sql); !result) {
return utils::failure(result.err());
}
}
// auto c = pool_.acquire();
// for (const auto &node: *schema_) {
// auto result = query::query::create()
// .table(node->name(), node->info().definition().columns())
// .execute(*c);
// if (!result) {
// return utils::failure(result.err());
// }
// }
// execute additional commands (e.g. ALTER TABLE ADD FK)
for (const auto &sql: fk_sql_commands) {
if (auto result = c->execute(sql); !result) {
return utils::failure(result.err());
}
}
return utils::ok<void>();
}
utils::result<void, utils::error> session::drop_table(const std::string &table_name) const {
@@ -6,22 +6,25 @@
namespace matador::query {
utils::result<size_t, utils::error> executable_query::execute(const sql::executor &exec) const
{
utils::result<size_t, utils::error> executable_query::execute(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
return exec.execute(compiler.compile(*context_, exec.dialect(), std::nullopt));
}
utils::result<sql::statement, utils::error> executable_query::prepare(const sql::executor &exec) const
{
utils::result<sql::statement, utils::error> executable_query::prepare(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Prepared;
return exec.prepare(compiler.compile(*context_, exec.dialect(), std::nullopt));
}
std::string executable_query::str(const sql::executor &exec) const
{
sql::query_context executable_query::compile( const sql::executor& exec ) const {
query_compiler compiler;
context_->mode = query_mode::Prepared;
return compiler.compile(*context_, exec.dialect(), std::nullopt);
}
std::string executable_query::str(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
return exec.str(compiler.compile(*context_, exec.dialect(), std::nullopt));
@@ -5,8 +5,7 @@
namespace matador::query {
utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const
{
utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
return exec.fetch(compiler.compile(*context_, exec.dialect(), std::nullopt))
@@ -15,8 +14,7 @@ utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fet
});
}
utils::result<std::optional<sql::record>, utils::error> fetchable_query::fetch_one(const sql::executor &exec) const
{
utils::result<std::optional<sql::record>, utils::error> fetchable_query::fetch_one(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
auto result = exec.fetch(compiler.compile(*context_, exec.dialect(), std::nullopt));
@@ -30,25 +28,22 @@ utils::result<std::optional<sql::record>, utils::error> fetchable_query::fetch_o
return utils::ok(std::optional<sql::record>{std::nullopt});
}
return utils::ok(std::optional<sql::record>{*first.get()});
return utils::ok(std::optional{*first.get()});
}
std::string fetchable_query::str(const sql::executor &exec) const
{
std::string fetchable_query::str(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
return exec.str(compiler.compile(*context_, exec.dialect(), std::nullopt));
}
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetchable_query::fetch(const sql::executor &exec) const
{
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetchable_query::fetch(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Direct;
return exec.fetch(compiler.compile(*context_, exec.dialect(), std::nullopt));
}
utils::result<sql::statement, utils::error> fetchable_query::prepare(const sql::executor &exec) const
{
utils::result<sql::statement, utils::error> fetchable_query::prepare(const sql::executor &exec) const {
query_compiler compiler;
context_->mode = query_mode::Prepared;
return exec.prepare(compiler.compile(*context_, exec.dialect(), std::nullopt));
+14 -20
View File
@@ -29,7 +29,7 @@ sql::query_context query_compiler::compile(const query_data &data,
dialect_ = nullptr;
data_ = nullptr;
return query_;
return {query_};
}
std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const query_data &data, const sql::column &col) {
@@ -52,7 +52,7 @@ std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const
void query_compiler::visit(internal::query_select_part &select_part)
{
query_.command = sql::sql_command::SQL_CMD_SELECT;
query_.command = sql::sql_command::SQL_SELECT;
query_.sql = dialect_->token_at(sql::dialect_token::SELECT) + " ";
query_.prototype.clear();
@@ -131,7 +131,7 @@ void query_compiler::visit(internal::query_limit_part &limit_part)
void query_compiler::visit(internal::query_insert_part &/*insert_part*/)
{
query_.command = sql::sql_command::SQL_CMD_INSERT;
query_.command = sql::sql_command::SQL_INSERT;
query_.sql = dialect_->token_at(sql::dialect_token::INSERT);
}
@@ -212,14 +212,14 @@ void query_compiler::visit(internal::query_values_part &values_part) {
void query_compiler::visit(internal::query_update_part &update_part)
{
query_.command = sql::sql_command::SQL_CMD_UPDATE;
query_.command = sql::sql_command::SQL_UPDATE;
query_.table = update_part.table();
query_.sql += query_compiler::build_table_name(update_part.token(), *dialect_, query_.table);
}
void query_compiler::visit(internal::query_delete_part &/*delete_part*/)
{
query_.command = sql::sql_command::SQL_CMD_DELETE;
query_.command = sql::sql_command::SQL_DELETE;
query_.sql = dialect_->token_at(sql::dialect_token::REMOVE);
}
@@ -231,7 +231,7 @@ void query_compiler::visit(internal::query_delete_from_part &delete_from_part)
void query_compiler::visit(internal::query_create_part &/*create_part*/)
{
query_.command = sql::sql_command::SQL_CMD_CREATE;
query_.command = sql::sql_command::SQL_CREATE_TABLE;
query_.sql = dialect_->token_at(sql::dialect_token::CREATE);
}
@@ -274,10 +274,13 @@ void query_compiler::visit(internal::query_create_table_part &create_table_part)
result.append(", CONSTRAINT PK_" + create_table_part.table().name + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
}
for (const auto &[column, reference_column]: context.foreign_contexts) {
result += ", CONSTRAINT FK_" + create_table_part.table().name;
result += "_" + column;
result += " FOREIGN KEY (" + dialect_->prepare_identifier_string(column) + ")";
result += " REFERENCES " + reference_column->table_name() + "(" + reference_column->name() + ")";
// ALTER TABLE Orders ADD CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID);
std::string fk_cmd = "ALTER TABLE " + query_.table.name + " ADD";
fk_cmd += " CONSTRAINT FK_" + create_table_part.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});
}
result += ")";
@@ -286,7 +289,7 @@ void query_compiler::visit(internal::query_create_table_part &create_table_part)
void query_compiler::visit(internal::query_drop_part &/*drop_part*/)
{
query_.command = sql::sql_command::SQL_CMD_DROP;
query_.command = sql::sql_command::SQL_DROP_TABLE;
query_.sql = dialect_->token_at(sql::dialect_token::DROP);
}
@@ -311,24 +314,15 @@ void query_compiler::visit(internal::query_set_part &set_part)
for (const auto &col: set_part.key_values()) {
result.append(dialect_->prepare_identifier_string(col.name()) + "=");
result.append(determine_set_value(visitor, col.value()));
// auto var = col.value();
// std::visit(visitor, var);
// result.append(visitor.result);
}
} else {
auto it = set_part.key_values().begin();
result.append(dialect_->prepare_identifier_string(it->name()) + "=");
result.append(determine_set_value(visitor, (it++)->value()));
// auto var = (it++)->value();
// std::visit(visitor, var);
// result.append(visitor.result);
for (; it != set_part.key_values().end(); ++it) {
result.append(", ");
result.append(dialect_->prepare_identifier_string(it->name()) + "=");
result.append(determine_set_value(visitor, it->value()));
// var = it->value();
// std::visit(visitor, var);
// result.append(visitor.result);
}
}
+11 -23
View File
@@ -3,8 +3,7 @@
#include "matador/sql/backend_provider.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/dialect_token.hpp"
// #include "matador/sql/schema.hpp"
// #include "matador/sql/query_compile_context.hpp"
#include "matador/sql/error_code.hpp"
#include "matador/sql/interface/connection_impl.hpp"
@@ -127,24 +126,20 @@ utils::result<void, utils::error> connection::rollback() const {
return utils::ok<void>();
}
utils::result<std::vector<object::attribute_definition>, utils::error> connection::describe(const std::string &table_name) const
{
utils::result<std::vector<object::attribute_definition>, utils::error> connection::describe(const std::string &table_name) const {
return connection_->describe(table_name);
}
utils::result<bool, utils::error> connection::exists(const std::string &schema_name, const std::string &table_name) const
{
utils::result<bool, utils::error> connection::exists(const std::string &schema_name, const std::string &table_name) const {
return connection_->exists(schema_name, table_name);
}
utils::result<bool, utils::error> connection::exists(const std::string &table_name) const
{
utils::result<bool, utils::error> connection::exists(const std::string &table_name) const {
return connection_->exists(dialect().default_schema_name(), table_name);
}
utils::result<size_t, utils::error> connection::execute(const std::string &sql) const
{
// logger_.debug(sql);
utils::result<size_t, utils::error> connection::execute(const std::string &sql) const {
logger_->on_execute(sql);
return connection_->execute(sql);
}
@@ -181,22 +176,18 @@ bool has_unknown_columns(const std::vector<object::attribute_definition> &column
// return query_result<record>{std::move(res), ctx.prototype};
// }
utils::result<std::unique_ptr<query_result_impl>, utils::error> connection::fetch(const query_context &ctx) const
{
// logger_.debug(sql);
utils::result<std::unique_ptr<query_result_impl>, utils::error> connection::fetch(const query_context &ctx) const {
logger_->on_fetch(ctx.sql);
return connection_->fetch(ctx);
// return connection_->fetch(dialect().compile(ctx, *connection_));
}
utils::result<size_t, utils::error> connection::execute(const query_context& ctx) const
{
return execute(ctx.sql);
// return execute(dialect().compile(ctx, *connection_).sql);
utils::result<size_t, utils::error> connection::execute(const query_context& ctx) const {
return execute(ctx.sql);
}
utils::result<statement, utils::error> connection::prepare(const query_context &ctx) const
{
if (ctx.command != sql_command::SQL_CMD_CREATE && (ctx.prototype.empty() || has_unknown_columns(ctx.prototype))) {
if (ctx.command != sql_command::SQL_CREATE_TABLE && (ctx.prototype.empty() || has_unknown_columns(ctx.prototype))) {
if (const auto result = describe(ctx.table.name); result.is_ok()) {
for (auto &col: ctx.prototype) {
const auto rit = std::find_if(std::begin(*result), std::end(*result),
@@ -210,9 +201,6 @@ utils::result<statement, utils::error> connection::prepare(const query_context &
}
}
// return connection_->prepare(qry).and_then([](auto &&res) {
// return statement(std::forward<decltype(res)>(res));
// });
if (auto result = connection_->prepare(ctx); result.is_ok()) {
return utils::ok(statement(result.release()));
}