#include "matador/sql/query_builder.hpp" #include "matador/sql/dialect.hpp" #include 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 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 columns) { return this->table(table, std::vector{columns}); } query_builder &query_builder::table(const std::string &table, const std::vector &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 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 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_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_))); 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; } }