#include "matador/query/query_builder.hpp" #include "matador/query/attribute_string_writer.hpp" #include "matador/query/query_data.hpp" #include "matador/query/criteria_evaluator.hpp" #include "matador/query/query_utils.hpp" #include "matador/query/table_constraint.hpp" #include "matador/query/table_column.hpp" #include "matador/query/internal/basic_type_to_string_visitor.hpp" #include "matador/query/internal/string_builder_utils.hpp" #include "matador/query/internal/query_parts.hpp" #include "matador/sql/query_context.hpp" #include "matador/sql/connection.hpp" #include "matador/sql/dialect.hpp" namespace matador::query { sql::query_context query_builder::compile(const query_data &data, const sql::dialect &d, const std::optional> conn) { data_ = &data; dialect_ = &d; connection_ = conn; query_ = {}; for (const auto &part: data.parts) { part->accept(*this); } finisher_(query_); connection_ = std::nullopt; dialect_ = nullptr; data_ = nullptr; return {query_}; } std::string handle_column(sql::query_context &ctx, const sql::dialect *d, const query_data &data, const table_column &col) { if (col.is_function()) { ctx.prototype.emplace_back(col.name()); ctx.prototype.back().change_type(utils::basic_type::Int32); } else { ctx.prototype.emplace_back(col.name()); } // if (col.table() != nullptr) { // if (const auto it = data.tables.find(col.table()->name()); it != data.tables.end()) { // return prepare_identifier(*d,col); // } // } return prepare_identifier(*d, col); } void query_builder::visit(internal::query_alter_part& part) { query_.sql = dialect_->token_at(part.token()); } void query_builder::visit(internal::query_alter_table_part& part) { query_.command = sql::sql_command::AlterTable; query_.sql += " " + dialect_->token_at(part.token()) + " " + dialect_->prepare_identifier_string(part.table().name()); } void query_builder::visit(internal::query_add_key_constraint_part& part) { query_.sql += " " + dialect_->add_constraint() + " " + part.name(); } void build_columns_with_name_only(std::string &out, const std::vector &cols, const sql::dialect &d); void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d); void query_builder::visit(internal::query_add_foreign_key_constraint_part& part) { query_.sql += " " + dialect_->token_at(part.token()) + " ("; build_columns(query_.sql, part.columns(), *dialect_); query_.sql += ")"; } void query_builder::visit(internal::query_add_primary_key_constraint_part& part) { query_.sql += " " + dialect_->primary_key() + " ("; build_columns(query_.sql, part.columns(), *dialect_); query_.sql += ")"; } void query_builder::visit(internal::query_add_foreign_key_reference_part& part) { query_.sql += " " + dialect_->token_at(part.token()) + " " + part.table().name() + " ("; build_columns(query_.sql, part.columns(), *dialect_); query_.sql += ")"; } void query_builder::visit(internal::query_add_constraint_part_by_constraint &part) { query_.sql += build_add_constraint_string(part.constraint()); } void query_builder::visit(internal::query_drop_key_constraint_part_by_name& part) { query_.sql += " " + dialect_->token_at(part.token()) + " " + part.name(); } void query_builder::visit(internal::query_drop_key_constraint_part_by_constraint& part) { query_.sql += " " + build_drop_constraint_string(part.constraint()); } void query_builder::visit(internal::query_select_part &part) { query_.command = sql::sql_command::Select; query_.sql = dialect_->select() + " "; query_.prototype.clear(); std::string result; if (part.columns().empty()) { 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 )); } } else { auto it = columns.begin(); result.append(handle_column(query_, dialect_, *data_, *it++)); for (; it != columns.end(); ++it) { result.append(", "); result.append(handle_column(query_, dialect_, *data_, *it)); } } query_.sql += result; } void query_builder::visit(internal::query_from_part &part) { query_.table_name = part.tables().front().name(); query_.sql += " " + dialect_->from() + " "; if (const auto &tables = part.tables(); tables.size() < 2) { for (const auto &tab: tables) { query_.sql += build_table_name(*dialect_, tab); } } else { auto it = tables.begin(); query_.sql.append(build_table_name(*dialect_, *it++)); for (; it != tables.end(); ++it) { query_.sql.append(", "); query_.sql.append(build_table_name(*dialect_, *it)); } } } void query_builder::visit(internal::query_join_table_part &part) { query_.sql += " " + build_table_name(part.token(), *dialect_, part.table()); } void query_builder::visit(internal::query_join_query_part &part) { query_.sql += " " + dialect_->join() + " (" + part.query().str(*dialect_) + ")"; } void query_builder::visit(internal::query_on_part &part) { criteria_evaluator evaluator(*dialect_, query_); query_.sql += " " + dialect_->on() + " " + evaluator.evaluate(part.condition()); } void query_builder::visit(internal::query_where_part &part) { criteria_evaluator evaluator(*dialect_, query_); query_.sql += " " + dialect_->where() + " " + evaluator.evaluate(part.condition()); } void query_builder::visit(internal::query_group_by_part &part) { query_.sql += " " + dialect_->group_by() + " "; if (part.columns().size() < 2) { for (const auto &col: part.columns()) { query_.sql.append(dialect_->prepare_identifier_string(col.name())); } } else { auto it = part.columns().begin(); query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); for (; it != part.columns().end(); ++it) { query_.sql.append(", "); query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); } } } void query_builder::visit(internal::query_order_by_part &part) { query_.sql += " " + dialect_->order_by() + " "; if (part.columns().size() < 2) { for (const auto &col: part.columns()) { query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name())); } } else { auto it = part.columns().begin(); query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); for (; it != part.columns().end(); ++it) { query_.sql.append(", "); query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); } } } void query_builder::visit(internal::query_order_by_asc_part &/*order_by_asc_part*/) { query_.sql += " " + dialect_->asc(); } void query_builder::visit(internal::query_order_by_desc_part &/*order_by_desc_part*/) { query_.sql += " " + dialect_->desc(); } void query_builder::visit(internal::query_offset_part &part) { query_.sql += " " + dialect_->offset() + " " + std::to_string(part.offset()); } void query_builder::visit(internal::query_limit_part &part) { query_.sql += " " + dialect_->limit() + " " + std::to_string(part.limit()); } void query_builder::visit(internal::query_insert_part &/*insert_part*/) { query_.command = sql::sql_command::Insert; query_.sql = dialect_->insert(); } void query_builder::visit(internal::query_into_part &part) { query_.table_name = part.table().name(); query_.sql += " " + dialect_->into() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; build_columns_with_name_only(query_.sql, part.columns(), *dialect_); query_.sql += ")"/* + result*/; } struct value_visitor { value_visitor(attribute_string_writer &w, sql::query_context &ctx) : value_to_string_visitor(w, ctx) {} void operator()(const utils::database_type &val) { std::visit(value_to_string_visitor, val); } void operator()(const utils::placeholder &/*val*/) { value_to_string_visitor.query.bind_vars.emplace_back(std::string("value_") + std::to_string(value_to_string_visitor.query.bind_vars.size() + 1)); value_to_string_visitor.result = value_to_string_visitor.writer->dialect().next_placeholder(value_to_string_visitor.query.bind_vars); } internal::basic_type_to_string_visitor value_to_string_visitor; }; std::string query_builder::determine_value(value_visitor &visitor, const std::variant &val) { std::visit(visitor, val); return visitor.value_to_string_visitor.result; } void query_builder::visit(internal::query_values_part &part) { query_.sql += " " + dialect_->values(); attribute_string_writer writer(*dialect_, connection_); value_visitor visitor(writer, query_); std::string result{"("}; if (part.values().size() < 2) { for (const auto& val: part.values()) { result.append(determine_value(visitor, val)); } } else { auto it = part.values().begin(); auto val = *it++; result.append(determine_value(visitor, val)); for (; it != part.values().end(); ++it) { result.append(", "); val = *it; result.append(determine_value(visitor, val)); } } result += (")"); query_.sql += " " + result; } void query_builder::visit(internal::query_update_part &part) { query_.command = sql::sql_command::Update; query_.table_name = part.table().name(); query_.sql += query_builder::build_table_name(part.token(), *dialect_, query_.table_name); } void query_builder::visit(internal::query_delete_part &/*delete_part*/) { query_.command = sql::sql_command::Delete; query_.sql = dialect_->remove(); } void query_builder::visit(internal::query_delete_from_part &part) { query_.table_name = part.table().name(); query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); } void query_builder::visit(internal::query_create_part &/*create_part*/) { query_.command = sql::sql_command::CreateTable; query_.sql = dialect_->create(); } void build_create_column(std::string &out, const table_column &col, const sql::dialect &d); std::string build_constraint(const table_constraint &cons, const sql::dialect &d); void query_builder::visit(internal::query_create_table_part &part) { query_.sql += " " + dialect_->table() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; query_.table_name = part.table().name(); finisher_ = [](sql::query_context &ctx) { ctx.sql += ")"; }; } void query_builder::visit(internal::query_create_table_columns_part& part) { bool first = true; for (const auto& col : part.columns()) { if (!first) { query_.sql.append(", "); } build_create_column(query_.sql, col, *dialect_); first = false; } } void query_builder::visit(internal::query_create_table_constraints_part& part) { std::string result; for (const auto& c : part.constraints()) { result.append(", "); result.append(build_constraint(c, *dialect_)); } query_.sql += result; } void query_builder::visit( internal::query_create_schema_part& part ) { query_.command = sql::sql_command::CreateSchema; query_.sql += " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); } void query_builder::visit(internal::query_drop_part &part) { query_.command = sql::sql_command::DropTable; query_.sql = dialect_->token_at(part.token()); } void query_builder::visit( internal::query_drop_schema_part& part ) { query_.sql += " " + dialect_->drop() + " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); } void query_builder::visit(internal::query_set_part &part) { query_.sql += " " + dialect_->set() + " "; attribute_string_writer writer(*dialect_, connection_); std::string result; value_visitor visitor(writer, query_); if (part.column_values().size() < 2) { for (const auto &column_value: part.column_values()) { result.append(dialect_->prepare_identifier_string(column_value.col().name()) + "="); result.append(determine_value(visitor, column_value.value())); } } else { auto it = part.column_values().begin(); result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); result.append(determine_value(visitor, (it++)->value())); for (; it != part.column_values().end(); ++it) { result.append(", "); result.append(dialect_->prepare_identifier_string(it->col().name()) + "="); result.append(determine_value(visitor, it->value())); } } query_.sql += result; } void query_builder::visit(internal::query_drop_table_part &part) { query_.table_name = part.table().name(); query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); } void build_create_column(std::string &out, const table_column &col, const sql::dialect &d) { prepare_identifier_string_append(out, col.canonical_name(), d); out += " " + d.data_type_at(col.type()); if (col.attributes().size() > 0) { out.append("(" + std::to_string(col.attributes().size()) + ")"); } if (!col.is_nullable()) { out.append(" ").append(d.not_null()); } if (is_constraint_set(col.attributes().options(), utils::constraints::Unique)) { out.append(" ").append(d.unique()); } if (is_constraint_set(col.attributes().options(), utils::constraints::PrimaryKey)) { out.append(" ").append(d.primary_key()); } } void build_columns_with_name_only(std::string &out, const std::vector &cols, const sql::dialect &d) { bool first = true; for (const auto& col : cols) { if (!first) { out.append(", "); } prepare_identifier_string_append(out, col.column_name(), d); first = false; } } void build_columns(std::string &out, const std::vector &cols, const sql::dialect &d) { bool first = true; for (const auto& col : cols) { if (!first) { out.append(", "); } prepare_identifier_string_append(out, col.name(), d); first = false; } } std::string build_constraint(const table_constraint& cons, const sql::dialect& d) { std::string result; if (!cons.name().empty()) { 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(")"); } else if (cons.is_foreign_key_constraint()) { result .append(d.foreign_key()) .append(" (") .append(cons.column_name()) .append(") ") .append(d.references()).append(" ") .append(cons.referenced_table()) .append(" (") .append(cons.referenced_column()) .append(")"); } else { // handle error } return result; } std::string query_builder::build_table_name(const sql::dialect_token token, const sql::dialect &d, const table& t) { return d.token_at(token) + " " + build_table_name(d, t); } std::string query_builder::build_table_name(const sql::dialect &d, const table& t) { return (!d.default_schema_name().empty() ? d.prepare_identifier_string(d.default_schema_name()) + "." : "") + d.prepare_identifier_string(t.table_name()) + (!t.has_alias() ? "" : " " + d.prepare_identifier_string(t.name())); } std::string query_builder::build_add_constraint_string(const table_constraint &c) const { std::string result = " " + dialect_->add_constraint() + " " + build_constraint_name(c) + " "; if (c.is_primary_key_constraint()) { result.append(dialect_->primary_key()).append(" (").append(c.column_name()).append(")"); } else if (c.is_foreign_key_constraint()) { result.append(dialect_->foreign_key()).append(" (").append(c.column_name()).append(") ").append(dialect_->references()).append(" ").append(c.referenced_table()).append(" (").append(c.referenced_column()).append(")"); } else if (c.is_unique_constraint()) { result.append(dialect_->unique()).append(" (").append(c.column_name()).append(")"); } return result; } std::string query_builder::build_drop_constraint_string(const table_constraint &c) const { return dialect_->drop_constraint() + " " + build_constraint_name(c); } std::string query_builder::build_constraint_name(const table_constraint &c) { if (c.is_primary_key_constraint()) { return "PK_" + c.table_name(); } if (c.is_foreign_key_constraint()) { return "FK_" + c.column_name() + "_" + c.table_name(); } if (c.is_unique_constraint()) { return "UK_" + c.column_name() + "_" + c.table_name(); } return ""; } }