query/source/orm/query/query_compiler.cpp

357 lines
12 KiB
C++

#include "matador/query/query_compiler.hpp"
#include "matador/query/attribute_string_writer.hpp"
#include "matador/query/query_data.hpp"
#include "matador/query/internal/basic_type_to_string_visitor.hpp"
#include "matador/query/internal/query_parts.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/utils/string.hpp"
namespace matador::query {
sql::query_context query_compiler::compile(const query_data &data,
const sql::dialect &d,
std::optional<std::reference_wrapper<const sql::connection_impl>> conn)
{
data_ = &data;
dialect_ = &d;
connection_ = conn;
query_ = {};
for (const auto &part: data.parts) {
part->accept(*this);
}
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 sql::column &col) {
ctx.result_vars.emplace_back(col.name);
const auto& column_table = col.table_.get();
ctx.column_aliases.insert({column_table->has_alias() ? column_table->alias : column_table->name + "." + col.name, col.alias});
if (col.is_function()) {
ctx.prototype.emplace_back(col.has_alias() ? col.alias : col.name);
ctx.prototype.back().type(utils::basic_type::type_int32);
} else {
ctx.prototype.emplace_back(col.name);
}
if (const auto it = data.tables.find(col.table_->name); it != data.tables.end()) {
return d->prepare_identifier({it->second, col.name, col.alias});
}
return d->prepare_identifier(col);
}
void query_compiler::visit(internal::query_select_part &select_part)
{
query_.command = sql::sql_command::SQL_CMD_SELECT;
query_.sql = dialect_->token_at(sql::dialect_token::SELECT) + " ";
query_.prototype.clear();
std::string result;
if (const auto &columns = select_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_compiler::visit(internal::query_from_part &from_part)
{
query_.table = from_part.table();
query_.sql += " " + query_compiler::build_table_name(from_part.token(), *dialect_, query_.table);
query_.table_aliases.insert({query_.table.name, query_.table.alias});
}
void query_compiler::visit(internal::query_join_part &join_part)
{
query_.sql += " " + query_compiler::build_table_name(join_part.token(), *dialect_, join_part.table());
}
void query_compiler::visit(internal::query_on_part &on_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::ON) +
" " + on_part.condition().evaluate(*dialect_, query_);
}
void query_compiler::visit(internal::query_where_part &where_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::WHERE) +
" " + where_part.condition().evaluate(*dialect_, query_);
}
void query_compiler::visit(internal::query_group_by_part &group_by_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::GROUP_BY) + " " + dialect_->prepare_identifier(group_by_part.column());
}
void query_compiler::visit(internal::query_order_by_part &order_by_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::ORDER_BY) +
" " + dialect_->prepare_condition(order_by_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);
}
void query_compiler::visit(internal::query_order_by_desc_part &/*order_by_desc_part*/)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::DESC);
}
void query_compiler::visit(internal::query_offset_part &offset_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::OFFSET) + " " + std::to_string(offset_part.offset());
}
void query_compiler::visit(internal::query_limit_part &limit_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::LIMIT) + " " + std::to_string(limit_part.limit());
}
void query_compiler::visit(internal::query_insert_part &/*insert_part*/)
{
query_.command = sql::sql_command::SQL_CMD_INSERT;
query_.sql = dialect_->token_at(sql::dialect_token::INSERT);
}
void query_compiler::visit(internal::query_into_part &into_part)
{
query_.table = into_part.table();
query_.sql += " " + dialect_->token_at(sql::dialect_token::INTO) +
" " + dialect_->prepare_identifier_string(into_part.table().name);
std::string result{"("};
if (into_part.columns().size() < 2) {
for (const auto &col: into_part.columns()) {
result.append(dialect_->prepare_identifier_string(col.name));
}
} else {
auto it = into_part.columns().begin();
result.append(dialect_->prepare_identifier_string((it++)->name));
for (; it != into_part.columns().end(); ++it) {
result.append(", ");
result.append(dialect_->prepare_identifier_string(it->name));
}
}
result += (")");
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("unknown");
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;
};
void query_compiler::visit(internal::query_values_part &values_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::VALUES);
attribute_string_writer writer(*dialect_, connection_);
value_visitor visitor(writer, query_);
std::string result{"("};
if (values_part.values().size() < 2) {
for (const auto& val: values_part.values()) {
std::visit(visitor, val);
result.append(visitor.value_to_string_visitor.result);
}
} else {
auto it = values_part.values().begin();
auto val = *it++;
std::visit(visitor, val);
result.append(visitor.value_to_string_visitor.result);
for (; it != values_part.values().end(); ++it) {
result.append(", ");
val = *it;
std::visit(visitor, val);
result.append(visitor.value_to_string_visitor.result);
}
}
result += (")");
query_.sql += " " + result;
}
void query_compiler::visit(internal::query_update_part &update_part)
{
query_.command = sql::sql_command::SQL_CMD_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_.sql = dialect_->token_at(sql::dialect_token::REMOVE);
}
void query_compiler::visit(internal::query_delete_from_part &delete_from_part)
{
query_.table = delete_from_part.table();
query_.sql += " " + query_compiler::build_table_name(delete_from_part.token(), *dialect_, query_.table);
}
void query_compiler::visit(internal::query_create_part &/*create_part*/)
{
query_.command = sql::sql_command::SQL_CMD_CREATE;
query_.sql = dialect_->token_at(sql::dialect_token::CREATE);
}
struct fk_context
{
std::string column;
std::string ref_table;
std::string ref_column;
};
struct column_context
{
std::vector<std::string> primary_keys;
std::vector<fk_context> foreign_contexts;
};
std::string build_create_column(const object::attribute_definition &col, const sql::dialect &d, column_context &context);
void query_compiler::visit(internal::query_create_table_part &create_table_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::TABLE) + " " + dialect_->prepare_identifier_string(create_table_part.table().name) + " ";
query_.table = create_table_part.table();
std::string result = "(";
column_context context;
if (create_table_part.columns().size() < 2) {
for (const auto &col: create_table_part.columns()) {
result.append(build_create_column(col, *dialect_, context));
}
} else {
auto it = create_table_part.columns().begin();
result.append(build_create_column(*it++, *dialect_, context));
for (; it != create_table_part.columns().end(); ++it) {
result.append(", ");
result.append(build_create_column(*it, *dialect_, context));
}
}
if (!context.primary_keys.empty()) {
result.append(", CONSTRAINT PK_" + create_table_part.table().name + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
}
for (const auto &fk: context.foreign_contexts) {
result += ", CONSTRAINT FK_" + create_table_part.table().name;
result += "_" + fk.column;
result += " FOREIGN KEY (" + fk.column + ")";
result += " REFERENCES " + fk.ref_table + "(" + fk.ref_column + ")";
}
result += ")";
query_.sql += result;
}
void query_compiler::visit(internal::query_drop_part &/*drop_part*/)
{
query_.command = sql::sql_command::SQL_CMD_DROP;
query_.sql = dialect_->token_at(sql::dialect_token::DROP);
}
void query_compiler::visit(internal::query_set_part &set_part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::SET) + " ";
attribute_string_writer writer(*dialect_, connection_);
internal::basic_type_to_string_visitor value_to_string(writer, query_);
std::string result;
if (set_part.key_values().size() < 2) {
for (const auto &col: set_part.key_values()) {
result.append(dialect_->prepare_identifier_string(col.name()) + "=");
auto var = col.value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
}
} else {
auto it = set_part.key_values().begin();
result.append(dialect_->prepare_identifier_string(it->name()) + "=");
auto var = (it++)->value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
for (; it != set_part.key_values().end(); ++it) {
result.append(", ");
result.append(dialect_->prepare_identifier_string(it->name()) + "=");
var = it->value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
}
}
query_.sql += result;
}
void query_compiler::visit(internal::query_drop_table_part &drop_table_part)
{
query_.table = drop_table_part.table();
query_.sql += " " + query_compiler::build_table_name(drop_table_part.token(), *dialect_, query_.table);
}
std::string build_create_column(const object::attribute_definition &col, const sql::dialect &d, column_context &context)
{
std::string result = d.prepare_identifier_string(col.name()) + " " + d.data_type_at(col.type());
if (col.attributes().size() > 0) {
result.append("(" + std::to_string(col.attributes().size()) + ")");
}
if (!col.is_nullable()) {
result.append(" NOT NULL");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) {
result.append(" UNIQUE");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
context.primary_keys.emplace_back(col.name());
}
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
}
return result;
}
std::string query_compiler::build_table_name(const sql::dialect_token token, const sql::dialect &d, const sql::table& t)
{
return d.token_at(token) + " " +
(!d.default_schema_name().empty() ? d.prepare_identifier_string(d.default_schema_name()) + "." : "") +
d.prepare_identifier_string(t.name) +
(t.alias.empty() ? "" : " " + d.prepare_identifier_string(t.alias));
}
}