#include "matador/sql/query_builder.hpp" #include "matador/sql/column_name_generator.hpp" #include "matador/sql/column.hpp" #include "matador/sql/dialect.hpp" #include "matador/utils/string.hpp" #include namespace matador::sql { namespace detail { any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query) : d(d), query(query) {} 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) + "'"; } void any_type_to_string_visitor::to_string(utils::blob &val) { // "This is a binary Data string" as binary data: // MySQL: X'5468697320697320612062616E617279204461746120737472696E67' // Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67' // MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 // Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' result = d.token_at(dialect::token_t::BEGIN_BINARY_DATA) + utils::to_string(val) + d.token_at(dialect::token_t::END_BINARY_DATA); } void any_type_to_string_visitor::to_string(placeholder &/*val*/) { query.bind_vars.emplace_back("unknown"); result = d.next_placeholder(query.bind_vars); } } // 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_JOIN, state_t::QUERY_FINISH}}, {state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}}, {state_t::QUERY_JOIN, {state_t::QUERY_ON}}, {state_t::QUERY_ON, {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_JOIN, 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_) {} query_builder &query_builder::create() { initialize(command_t::CREATE, state_t::QUERY_CREATE); query_parts_.emplace_back(dialect::token_t::CREATE, 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_t::DROP, dialect_.token_at(dialect::token_t::DROP)); return *this; } query_builder &query_builder::select(std::initializer_list columns) { return select(std::vector{columns}); } query_builder &query_builder::select(const std::vector &columns) { initialize(command_t::SELECT, state_t::QUERY_SELECT); query_parts_.emplace_back(dialect::token_t::SELECT, dialect_.token_at(dialect::token_t::SELECT) + " "); query_.prototype.clear(); std::string result; if (columns.size() < 2) { for (const auto &col: columns) { result.append(dialect_.prepare_identifier(col)); query_.result_vars.emplace_back(col.name); query_.prototype.emplace_back(col.name); } } else { auto it = columns.begin(); result.append(dialect_.prepare_identifier(*it)); query_.result_vars.emplace_back(it->name); query_.prototype.emplace_back((*it++).name); for (; it != columns.end(); ++it) { result.append(", "); result.append(dialect_.prepare_identifier(*it)); query_.result_vars.emplace_back(it->name); query_.prototype.emplace_back((*it).name); } } query_parts_.emplace_back(dialect::token_t::COLUMNS, result); return *this; } query_builder &query_builder::insert() { initialize(command_t::INSERT, state_t::QUERY_INSERT); query_parts_.emplace_back(dialect::token_t::INSERT, 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_.table = {table}; query_parts_.emplace_back(dialect::token_t::UPDATE, 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_t::REMOVE, 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}); } struct fk_context { std::string column; std::string ref_table; std::string ref_column; }; struct column_context { std::vector primary_keys; std::vector foreign_contexts; }; //std::string build_create_column(const column_definition &col, const dialect &d, column_context &context); 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_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); query_.table = {table}; std::string result = "("; column_context context; // if (columns.size() < 2) { // for (const auto &col: columns) { // result.append(build_create_column(col, dialect_, context)); // } // } else { // auto it = columns.begin(); // result.append(build_create_column(*it++, dialect_, context)); // for (; it != columns.end(); ++it) { // result.append(", "); // result.append(build_create_column(*it, dialect_, context)); // } // } if (!context.primary_keys.empty()) { result.append(", CONSTRAINT PK_" + table + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")"); } for (const auto &fk: context.foreign_contexts) { result += ", CONSTRAINT FK_" + table; result += "_" + fk.column; result += " FOREIGN KEY (" + fk.column + ")"; result += " REFERENCES " + fk.ref_table + "(" + fk.ref_column + ")"; } result += ")"; query_parts_.emplace_back(dialect::token_t::COLUMNS, 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_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table)); query_.table = {table}; return *this; } query_builder &query_builder::into(const std::string &table, std::initializer_list column_names) { return into(table, std::vector{column_names}); } query_builder &query_builder::into(const std::string &table, const std::vector &column_names) { transition_to(state_t::QUERY_INTO); query_parts_.emplace_back(dialect::token_t::INTO, " " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " "); query_.table = {table}; std::string result{"("}; if (column_names.size() < 2) { for (const auto &col: column_names) { result.append(dialect_.prepare_identifier(col.name)); } } else { auto it = column_names.begin(); result.append(dialect_.prepare_identifier((it++)->name)); for (; it != column_names.end(); ++it) { result.append(", "); result.append(dialect_.prepare_identifier(it->name)); } } result += (")"); query_parts_.emplace_back(dialect::token_t::COLUMNS, result); return *this; } query_builder &query_builder::values(std::initializer_list values) { return this->values(std::vector{values}); } query_builder &query_builder::values(const std::vector &values) { transition_to(state_t::QUERY_VALUES); query_parts_.emplace_back(dialect::token_t::VALUES, " " + 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(dialect::token_t::INSERT_VALUES, result); return *this; } query_builder &query_builder::from(const std::string &table, const std::string &as) { transition_to(state_t::QUERY_FROM); if (dialect_.default_schema_name().empty()) { query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); } else { query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(dialect_.default_schema_name()) + "." + dialect_.prepare_identifier(table) + (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); } query_.table = {table}; return *this; } query_builder &query_builder::join(const std::string &table, join_type_t, const std::string &as) { transition_to(state_t::QUERY_JOIN); query_parts_.emplace_back(dialect::token_t::JOIN, " " + dialect_.token_at(dialect::token_t::JOIN) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); return *this; } query_builder &query_builder::on(const std::string &column, const std::string &join_column) { transition_to(state_t::QUERY_ON); query_parts_.emplace_back(dialect::token_t::ON, " " + dialect_.token_at(dialect::token_t::ON) + " " + dialect_.prepare_identifier(column) + "=" + dialect_.prepare_identifier(join_column)); return *this; } query_builder &query_builder::set(std::initializer_list key_values) { return set(std::vector{key_values}); } query_builder &query_builder::set(const std::vector &key_values) { transition_to(state_t::QUERY_SET); query_parts_.emplace_back(dialect::token_t::SET, " " + 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(dialect::token_t::UPDATE_VALUES, result); return *this; } query_builder &query_builder::where(const basic_condition &cond) { transition_to(state_t::QUERY_WHERE); query_parts_.emplace_back(dialect::token_t::WHERE, " " + dialect_.token_at(dialect::token_t::WHERE) + " "); query_parts_.emplace_back(dialect::token_t::WHERE_CLAUSE, cond.evaluate(const_cast(dialect_), query_)); 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_t::ORDER_BY, " " + 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_t::GROUP_BY, " " + 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_t::ASC, " " + 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_t::DESC, " " + 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_t::OFFSET, " " + 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_t::LIMIT, " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(count)); return *this; } query_context query_builder::compile() { for (const auto &part: query_parts_) { query_.sql.append(part.part); } query_.command_name = command_strings_[command_]; return query_; } 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; query_ = {}; state_ = state; query_parts_.clear(); } //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; //} column alias(const std::string &column, const std::string &as) { return {"", column, as}; } column alias(column &&col, const std::string &as) { col.as(as); return std::move(col); } column count(const std::string &column) { return {sql_function_t::COUNT, column}; } column count_all() { return count("*"); } }