query/src/sql/query_builder.cpp

382 lines
12 KiB
C++

#include "matador/sql/query_builder.hpp"
#include "matador/sql/dialect.hpp"
#include <stdexcept>
namespace matador::sql {
namespace detail {
void any_type_to_string_visitor::to_string(const char *val)
{
result = "'" + d.prepare_literal(val) + "'";
}
void any_type_to_string_visitor::to_string(std::string &val)
{
result = "'" + d.prepare_literal(val) + "'";
}
}
std::string build_create_column(const column &col, const dialect &d);
// poor mens state machine
// but does the job for query
query_builder::query_state_transition_map query_builder::transitions_{
{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}},
{state_t::QUERY_CREATE, {state_t::QUERY_TABLE_CREATE}},
{state_t::QUERY_DROP, {state_t::QUERY_TABLE_DROP}},
{state_t::QUERY_SELECT, {state_t::QUERY_FROM}},
{state_t::QUERY_INSERT, {state_t::QUERY_INTO}},
{state_t::QUERY_UPDATE, {state_t::QUERY_SET}},
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
{state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}},
{state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}},
{state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}},
{state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}},
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
{state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
{state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}},
{state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
{state_t::QUERY_GROUP_BY, {state_t::QUERY_ORDER_BY, state_t::QUERY_FINISH}},
{state_t::QUERY_OFFSET, {state_t::QUERY_LIMIT}},
{state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}},
{state_t::QUERY_VALUES, {state_t::QUERY_FINISH}},
{state_t::QUERY_FINISH, {}},
};
query_builder::query_state_to_string_map query_builder::state_strings_ {
{ state_t::QUERY_INIT, "init" },
{ state_t::QUERY_CREATE, "create" },
{ state_t::QUERY_DROP, "drop" },
{ state_t::QUERY_SELECT, "select" },
{ state_t::QUERY_INSERT, "insert" },
{ state_t::QUERY_UPDATE, "update" },
{ state_t::QUERY_DELETE, "delete" },
{ state_t::QUERY_TABLE_CREATE, "table" },
{ state_t::QUERY_TABLE_DROP, "table" },
{ state_t::QUERY_FROM, "from" },
{ state_t::QUERY_SET, "set" },
{ state_t::QUERY_INTO, "into" },
{ state_t::QUERY_WHERE, "where" },
{ state_t::QUERY_ORDER_BY, "order_by" },
{ state_t::QUERY_GROUP_BY, "group_by" },
{ state_t::QUERY_OFFSET, "offset" },
{ state_t::QUERY_LIMIT, "limit" },
{ state_t::QUERY_FINISH, "finish" },
};
query_builder::query_command_to_string_map query_builder::command_strings_ {
{ command_t::UNKNOWN, "unknown" },
{ command_t::CREATE, "create" },
{ command_t::DROP, "drop" },
{ command_t::SELECT, "select" },
{ command_t::INSERT, "insert" },
{ command_t::UPDATE, "update" },
{ command_t::REMOVE, "remove" },
};
query_builder::query_builder(const dialect &d)
: dialect_(d)
, value_to_string_(d) {}
query_builder& query_builder::create() {
initialize(command_t::CREATE, state_t::QUERY_CREATE);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE));
return *this;
}
query_builder& query_builder::drop() {
initialize(command_t::DROP, state_t::QUERY_DROP);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP));
return *this;
}
query_builder& query_builder::select(std::initializer_list<std::string> column_names) {
initialize(command_t::SELECT, state_t::QUERY_SELECT);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " ");
std::string result;
if (column_names.size() < 2) {
for (const auto &col : column_names) {
result.append(dialect_.prepare_identifier(col));
}
} else {
auto it = column_names.begin();
result.append(dialect_.prepare_identifier(*it++));
for (; it != column_names.end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(*it));
}
}
query_parts_.emplace_back(result);
return *this;
}
query_builder& query_builder::insert() {
initialize(command_t::INSERT, state_t::QUERY_INSERT);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT));
return *this;
}
query_builder& query_builder::update(const std::string &table) {
initialize(command_t::UPDATE, state_t::QUERY_UPDATE);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table));
return *this;
}
query_builder& query_builder::remove() {
initialize(command_t::REMOVE, state_t::QUERY_DELETE);
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE));
return *this;
}
query_builder& query_builder::table(const std::string &table, std::initializer_list<column> columns)
{
return this->table(table, std::vector<column>{columns});
}
query_builder &query_builder::table(const std::string &table, const std::vector<column> &columns) {
transition_to(state_t::QUERY_TABLE_CREATE);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
std::string result = "(";
if (columns.size() < 2) {
for (const auto &col : columns) {
result.append(build_create_column(col, dialect_));
}
} else {
auto it = columns.begin();
result.append(build_create_column(*it++, dialect_));
for (; it != columns.end(); ++it) {
result.append(", ");
result.append(build_create_column(*it, dialect_));
}
}
result += ")";
query_parts_.emplace_back(result);
return *this;
}
query_builder& query_builder::table(const std::string &table) {
transition_to(state_t::QUERY_TABLE_DROP);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
return *this;
}
query_builder& query_builder::into(const std::string &table, std::initializer_list<std::string> column_names) {
transition_to(state_t::QUERY_INTO);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " ");
std::string result{"("};
if (column_names.size() < 2) {
for (const auto &col : column_names) {
result.append(dialect_.prepare_identifier(col));
}
} else {
auto it = column_names.begin();
result.append(dialect_.prepare_identifier(*it++));
for (; it != column_names.end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(*it));
}
}
result += (")");
query_parts_.emplace_back(result);
return *this;
}
query_builder& query_builder::values(std::initializer_list<any_type> values) {
transition_to(state_t::QUERY_VALUES);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " ");
std::string result{"("};
if (values.size() < 2) {
for (auto val : values) {
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
}
} else {
auto it = values.begin();
auto val = *it++;
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
for (; it != values.end(); ++it) {
result.append(", ");
val = *it;
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
}
}
result += (")");
query_parts_.emplace_back(result);
return *this;
}
query_builder& query_builder::from(const std::string &table, const std::string &as) {
transition_to(state_t::QUERY_FROM);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as));
return *this;
}
query_builder &query_builder::join(const std::string &table, join_type_t)
{
return *this;
}
query_builder &query_builder::on(const std::string &column, const std::string &join_column)
{
return *this;
}
query_builder& query_builder::set(std::initializer_list<key_value_pair> key_values) {
transition_to(state_t::QUERY_SET);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " ");
std::string result;
if (key_values.size() < 2) {
for (const auto &col : 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 = 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 != 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_parts_.emplace_back(result);
return *this;
}
query_builder& query_builder::where(const basic_condition &cond) {
transition_to(state_t::QUERY_WHERE);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::WHERE) + " ");
query_parts_.emplace_back(cond.evaluate(const_cast<dialect &>(dialect_)));
return *this;
}
query_builder& query_builder::order_by(const std::string& column) {
transition_to(state_t::QUERY_ORDER_BY);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ORDER_BY) + " " + dialect_.prepare_identifier(column));
return *this;
}
query_builder& query_builder::group_by(const std::string& column) {
transition_to(state_t::QUERY_GROUP_BY);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(column));
return *this;
}
query_builder& query_builder::asc() {
transition_to(state_t::QUERY_ORDER_DIRECTION);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ASC));
return *this;
}
query_builder& query_builder::desc() {
transition_to(state_t::QUERY_ORDER_DIRECTION);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::DESC));
return *this;
}
query_builder& query_builder::offset( size_t count ) {
transition_to(state_t::QUERY_OFFSET);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(count));
return *this;
}
query_builder& query_builder::limit( size_t count ) {
transition_to(state_t::QUERY_LIMIT);
query_parts_.emplace_back( " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string( count));
return *this;
}
std::string query_builder::compile() {
std::string result;
for (const auto &part : query_parts_) {
result.append(part);
}
return result;
}
void query_builder::transition_to(query_builder::state_t next)
{
if (transitions_[state_].count(next) == 0) {
throw std::logic_error("invalid next state " + state_strings_[next]);
}
state_ = next;
}
void query_builder::initialize(query_builder::command_t cmd, query_builder::state_t state)
{
command_ = cmd;
state_ = state;
query_parts_.clear();
}
std::string build_create_column(const column &col, const dialect &d) {
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 (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
result.append(" NOT NULL");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
result.append(" PRIMARY KEY");
}
return result;
}
}