ensure proper table creation in session::create_schema including foreign key constraints
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
attribute_definition_generator::attribute_definition_generator(std::vector<object::attribute_definition> &columns, schema &repo)
|
||||
attribute_definition_generator::attribute_definition_generator(std::vector<object::attribute_definition> &columns, const schema &repo)
|
||||
: columns_(columns)
|
||||
, repo_(repo)
|
||||
{}
|
||||
@@ -23,7 +23,7 @@ utils::result<std::shared_ptr<attribute_definition>, utils::error> attribute_def
|
||||
}
|
||||
|
||||
void attribute_definition_generator::insert_missing_reference_column(const std::type_index& ti, std::shared_ptr<attribute_definition> ref_column) const {
|
||||
repo_.missing_references_.insert({ti, ref_column});
|
||||
const_cast<schema&>(repo_).missing_references_.insert({ti, ref_column});
|
||||
}
|
||||
|
||||
void fk_attribute_generator::on_primary_key(const char *, std::string &, const size_t size)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace matador::utils {
|
||||
|
||||
std::string error::message() const {
|
||||
return error_message_;
|
||||
return ec_.message();;
|
||||
}
|
||||
|
||||
std::string error::category() const {
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user