query/src/sql/query_compiler.cpp

309 lines
9.9 KiB
C++

#include "matador/sql/query_compiler.hpp"
#include "matador/sql/query_data.hpp"
#include "matador/sql/column_definition.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_builder.hpp"
#include "matador/utils/string.hpp"
namespace matador::sql {
query_compiler::query_compiler(const sql::dialect &d)
: dialect_(d)
{}
query_context query_compiler::compile(const query_data *data)
{
for (const auto &part: data->parts) {
part->accept(*this);
}
return query_;
}
void query_compiler::visit(query_select_part &select_part)
{
query_.sql = dialect_.token_at(dialect::token_t::SELECT) + " ";
query_.prototype.clear();
std::string result;
const auto &columns = select_part.columns();
if (columns.size() < 2) {
for (const auto &col: columns) {
result.append(dialect_.prepare_identifier(col));
query_.result_vars.emplace_back(col.name);
query_.prototype.append(column_definition{col.name});
}
} else {
auto it = columns.begin();
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name);
query_.prototype.append(column_definition{(*it++).name});
for (; it != columns.end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name);
query_.prototype.append(column_definition{(*it).name});
}
}
query_.sql += result;
}
void query_compiler::visit(query_from_part &from_part)
{
query_.table_name = from_part.table().name;
if (dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " +
dialect_.prepare_identifier(from_part.table().alias));
} else {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
"." + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " +
dialect_.prepare_identifier(from_part.table().alias));
}
}
void query_compiler::visit(query_join_part &join_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(join_part.table().name) +
(join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias));
}
void query_compiler::visit(query_on_part &on_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ON) +
" " + on_part.condition().evaluate(const_cast<dialect &>(dialect_), query_);
}
void query_compiler::visit(query_where_part &where_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::WHERE) +
" " + where_part.condition().evaluate(const_cast<dialect &>(dialect_), query_);
}
void query_compiler::visit(query_group_by_part &group_by_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(group_by_part.column());
}
void query_compiler::visit(query_order_by_part &order_by_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ORDER_BY) +
" " + dialect_.prepare_identifier(order_by_part.column());
}
void query_compiler::visit(query_order_by_asc_part &order_by_asc_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ASC);
}
void query_compiler::visit(query_order_by_desc_part &order_by_desc_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::DESC);
}
void query_compiler::visit(query_offset_part &offset_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(offset_part.offset());
}
void query_compiler::visit(query_limit_part &limit_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(limit_part.limit());
}
void query_compiler::visit(query_insert_part &insert_part)
{
query_.sql = dialect_.token_at(dialect::token_t::INSERT);
}
void query_compiler::visit(query_into_part &into_part)
{
query_.table_name = into_part.table().name;
query_.sql += " " + dialect_.token_at(dialect::token_t::INTO) +
" " + dialect_.prepare_identifier(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(col.name));
}
} else {
auto it = into_part.columns().begin();
result.append(dialect_.prepare_identifier((it++)->name));
for (; it != into_part.columns().end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(it->name));
}
}
result += (")");
query_.sql += " " + result;
}
void query_compiler::visit(query_values_part &values_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::VALUES);
detail::any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result{"("};
if (values_part.values().size() < 2) {
for (auto val: values_part.values()) {
std::visit(value_to_string, val);
result.append(value_to_string.result);
}
} else {
auto it = values_part.values().begin();
auto val = *it++;
std::visit(value_to_string, val);
result.append(value_to_string.result);
for (; it != values_part.values().end(); ++it) {
result.append(", ");
val = *it;
std::visit(value_to_string, val);
result.append(value_to_string.result);
}
}
result += (")");
query_.sql += " " + result;
}
void query_compiler::visit(query_update_part &update_part)
{
query_.table_name = update_part.table().name;
query_.sql = dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(update_part.table().name);
}
void query_compiler::visit(query_delete_part &delete_part)
{
}
void query_compiler::visit(query_create_part &create_part)
{
query_.sql = dialect_.token_at(dialect::token_t::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 column_definition &col, const dialect &d, column_context &context);
void query_compiler::visit(query_create_table_part &create_table_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(create_table_part.table().name) + " ";
query_.table_name = create_table_part.table().name;
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(query_drop_part &drop_part)
{
query_.sql = dialect_.token_at(dialect::token_t::DROP);
}
void query_compiler::visit(query_set_part &set_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::SET) + " ";
detail::any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result;
if (set_part.key_values().size() < 2) {
for (const auto &col: set_part.key_values()) {
result.append(dialect_.prepare_identifier(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(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((*it).name()) + "=");
var = it->value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
}
}
query_.sql += result;
}
void query_compiler::visit(query_drop_table_part &drop_table_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(drop_table_part.table().name);
query_.table_name = drop_table_part.table().name;
}
std::string build_create_column(const column_definition &col, const dialect &d, column_context &context)
{
std::string result = d.prepare_identifier(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;
}
}