diff --git a/include/matador/query/query_utils.hpp b/include/matador/query/query_utils.hpp index d2cccb6..a3d71d4 100644 --- a/include/matador/query/query_utils.hpp +++ b/include/matador/query/query_utils.hpp @@ -17,5 +17,43 @@ void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_ void prepare_column(std::string &out, const sql::dialect& d, const table_column &col); [[nodiscard]] std::string prepare_identifier(const sql::dialect& d, const table_column &col); [[nodiscard]] std::string prepare_criteria(const sql::dialect& d, const table_column &col); + +[[nodiscard]] std::string to_query_string(const utils::value &val, const sql::dialect& d); + +/** + * Prepare string literal + * + * @param str String literal to be prepared + * @param d The SQL dialect to use preparing the literal + */ +[[nodiscard]] std::string prepare_literal(const std::string &str, const sql::dialect& d); + +/** + * Prepare SQL dialect identifier for execution + * and escape quotes and quote the identifier + * string + * + * @param col The identifier string to be prepared + * @param d The SQL dialect to use preparing the identifier string + * @return The prepared string + */ +[[nodiscard]] std::string prepare_identifier_string(const std::string &col, const sql::dialect& d); + +/** + * Escape identifier quotes inside identifiers. + * + * @param str Identifier to be escaped + * @param d The SQL dialect to use for escaping + */ +void escape_quotes_in_identifier(std::string &str, const sql::dialect& d); + +/** + * Escape quotes in string literals + * + * @param str String literal to be escaped + * @param d The SQL dialect to use for escaping + */ +void escape_quotes_in_literals(std::string &str, const sql::dialect& d); + } #endif //MATADOR_QUERY_UTILS_HPP \ No newline at end of file diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index c3aeb5a..5638598 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -21,15 +21,14 @@ namespace matador::sql { class connection_impl; -class dialect final -{ +class dialect final { public: /** * Holding enums concerning escaping identifiers */ enum class escape_identifier_t : uint8_t { - ESCAPE_BOTH_SAME, /**< The escape quotes are the same */ - ESCAPE_CLOSING_BRACKET /**< The escape quotes differ; escape the closing one */ + EscapeBothSame, /**< The escape quotes are the same */ + EscapeClosingBracket /**< The escape quotes differ; escape the closing one */ }; using token_to_string_map = std::unordered_map; @@ -44,50 +43,10 @@ public: [[nodiscard]] const std::string& data_type_at(utils::basic_type type) const; [[nodiscard]] const std::string& sql_function_at(sql_function_t func) const; - /** - * Prepare sql dialect identifier for execution - * and escape quotes and quote the identifier - * string - * - * @param col The identifier string to be prepared - * @return The prepared string - */ - [[nodiscard]] std::string prepare_identifier_string(const std::string &col) const; - [[nodiscard]] const std::string& to_string(bool val) const; - [[nodiscard]] std::string to_sql_string(const utils::value &val) const ; - void bool_strings(const std::string &true_string, const std::string &false_string); - /** - * Prepare string literal - * - * @param str String literal to be prepared - */ - [[nodiscard]] std::string prepare_literal(const std::string &str) const; - - /** - * Wrap identifier quotes around a SQL identifier keyword - * - * @param str Identifier to put quotes around - */ - void quote_identifier(std::string &str) const; - - /** - * Escape identifier quotes inside identifiers. - * - * @param str Identifier to be escaped - */ - void escape_quotes_in_identifier(std::string &str) const; - - /** - * Escape quotes in string literals - * - * @param str String literal to be escaped - */ - void escape_quotes_in_literals(std::string &str) const; - /** * Returns how the identifier quotes should be * escaped. @@ -192,7 +151,7 @@ private: next_placeholder_func placeholder_func_ = [](size_t) { return "?"; }; to_escaped_string_func to_escaped_string_func_ = [](const utils::blob_type_t &val) { return utils::to_string(val); }; - escape_identifier_t identifier_escape_type_ = escape_identifier_t::ESCAPE_BOTH_SAME; + escape_identifier_t identifier_escape_type_ = escape_identifier_t::EscapeBothSame; std::string default_schema_name_; diff --git a/source/orm/query/attribute_string_writer.cpp b/source/orm/query/attribute_string_writer.cpp index b8bc92a..9ce0af6 100644 --- a/source/orm/query/attribute_string_writer.cpp +++ b/source/orm/query/attribute_string_writer.cpp @@ -1,20 +1,19 @@ #include "matador/query/attribute_string_writer.hpp" +#include "matador/query/query_utils.hpp" + #include "matador/sql/interface/connection_impl.hpp" -#include - - #include "matador/sql/dialect.hpp" +#include #include "matador/utils/string.hpp" namespace matador::query { attribute_string_writer::attribute_string_writer(const sql::dialect &d, const std::optional > conn) + sql::connection_impl> > conn) : dialect_(d) - , conn_(conn) { -} +, conn_(conn) {} const sql::dialect &attribute_string_writer::dialect() const { return dialect_; @@ -70,11 +69,11 @@ void attribute_string_writer::write_value(size_t /*pos*/, const double &x) { } void attribute_string_writer::write_value(size_t /*pos*/, const utils::date_type_t &x) { - result_ = "'" + dialect_.prepare_literal(utils::to_string(x)) + "'"; + result_ = "'" + prepare_literal(utils::to_string(x), dialect_) + "'"; } void attribute_string_writer::write_value(size_t /*pos*/, const utils::time_type_t &x) { - result_ = "'" + dialect_.prepare_literal(utils::to_string(x)) + "'"; + result_ = "'" + prepare_literal(utils::to_string(x), dialect_) + "'"; } void attribute_string_writer::write_value(size_t /*pos*/, const utils::timestamp_type_t &/*x*/) { @@ -89,11 +88,11 @@ void attribute_string_writer::write_value(size_t pos, const char *x, size_t /*si } void attribute_string_writer::write_value(size_t /*pos*/, const std::string &x) { - result_ = "'" + dialect_.prepare_literal(x) + "'"; + result_ = "'" + prepare_literal(x, dialect_) + "'"; } void attribute_string_writer::write_value(size_t /*pos*/, const std::string &x, size_t /*size*/) { - result_ = "'" + dialect_.prepare_literal(x) + "'"; + result_ = "'" + prepare_literal(x, dialect_) + "'"; } void attribute_string_writer::write_value(size_t /*pos*/, const utils::blob_type_t &x) { diff --git a/source/orm/query/criteria_evaluator.cpp b/source/orm/query/criteria_evaluator.cpp index 1e626c6..70fde2c 100644 --- a/source/orm/query/criteria_evaluator.cpp +++ b/source/orm/query/criteria_evaluator.cpp @@ -126,7 +126,7 @@ void criteria_evaluator::visit(const not_criteria &node) { void criteria_evaluator::evaluate_value(const criteria_value &value) { std::visit(overload{ - [this](const utils::value& val){ clause_ += dialect_.to_sql_string(val); }, + [this](const utils::value& val){ clause_ += to_query_string(val, dialect_); }, [this](const utils::placeholder&) { clause_ += dialect_.next_placeholder(query_.bind_vars); } }, value); diff --git a/source/orm/query/query_builder.cpp b/source/orm/query/query_builder.cpp index 28f0c33..8ae12c0 100644 --- a/source/orm/query/query_builder.cpp +++ b/source/orm/query/query_builder.cpp @@ -49,7 +49,7 @@ void query_builder::visit(internal::query_alter_part& part) { void query_builder::visit(internal::query_alter_table_part& part) { query_.command = sql::sql_command::AlterTable; query_.sql += " " + dialect_->token_at(part.token()) + " " + - dialect_->prepare_identifier_string(part.table().name()); + prepare_identifier_string(part.table().name(), *dialect_); } void query_builder::visit(internal::query_add_key_constraint_part& part) { @@ -150,14 +150,14 @@ void query_builder::visit(internal::query_group_by_part& part) { query_.sql += " " + dialect_->group_by() + " "; if (part.columns().size() < 2) { for (const auto& col : part.columns()) { - query_.sql.append(dialect_->prepare_identifier_string(col.name())); + query_.sql.append(prepare_identifier_string(col.name(), *dialect_)); } } else { auto it = part.columns().begin(); - query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); + query_.sql.append(prepare_identifier_string((it++)->canonical_name(), *dialect_)); for (; it != part.columns().end(); ++it) { query_.sql.append(", "); - query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); + query_.sql.append(prepare_identifier_string(it->canonical_name(), *dialect_)); } } } @@ -166,14 +166,14 @@ void query_builder::visit(internal::query_order_by_part& part) { query_.sql += " " + dialect_->order_by() + " "; if (part.columns().size() < 2) { for (const auto& col : part.columns()) { - query_.sql.append(dialect_->prepare_identifier_string(col.canonical_name())); + query_.sql.append(prepare_identifier_string(col.canonical_name(), *dialect_)); } } else { auto it = part.columns().begin(); - query_.sql.append(dialect_->prepare_identifier_string((it++)->canonical_name())); + query_.sql.append(prepare_identifier_string((it++)->canonical_name(), *dialect_)); for (; it != part.columns().end(); ++it) { query_.sql.append(", "); - query_.sql.append(dialect_->prepare_identifier_string(it->canonical_name())); + query_.sql.append(prepare_identifier_string(it->canonical_name(), *dialect_)); } } } @@ -202,7 +202,7 @@ void query_builder::visit(internal::query_insert_part&/*insert_part*/) { void query_builder::visit(internal::query_into_part& part) { query_.table_name = part.table().name(); query_.sql += " " + dialect_->into() + - " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; + " " + prepare_identifier_string(part.table().name(), *dialect_) + " ("; build_columns_with_name_only(query_.sql, part.columns(), *dialect_); query_.sql += ")"/* + result*/; @@ -300,7 +300,7 @@ void build_create_column(std::string& out, const table_column& col, const sql::d std::string build_constraint(const table_constraint& cons, const sql::dialect& d); void query_builder::visit(internal::query_create_table_part& part) { - query_.sql += " " + dialect_->table() + " " + dialect_->prepare_identifier_string(part.table().name()) + " ("; + query_.sql += " " + dialect_->table() + " " + prepare_identifier_string(part.table().name(), *dialect_) + " ("; query_.table_name = part.table().name(); finisher_ = [](sql::query_context& ctx) { ctx.sql += ")"; }; @@ -328,12 +328,12 @@ void query_builder::visit(internal::query_create_table_constraints_part& part) { void query_builder::visit(internal::query_create_sequence_part& part) { query_.command = sql::sql_command::CreateSequence; - query_.sql += " " + dialect_->sequence() + " " + dialect_->prepare_identifier_string(part.sequence_name()); + query_.sql += " " + dialect_->sequence() + " " + prepare_identifier_string(part.sequence_name(), *dialect_); } void query_builder::visit(internal::query_create_schema_part& part) { query_.command = sql::sql_command::CreateSchema; - query_.sql += " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); + query_.sql += " " + dialect_->schema() + " " + prepare_identifier_string(part.schema(), *dialect_); } void query_builder::visit(internal::query_drop_part& part) { @@ -343,7 +343,7 @@ void query_builder::visit(internal::query_drop_part& part) { void query_builder::visit(internal::query_drop_schema_part& part) { query_.sql += " " + dialect_->drop() + " " + - dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); + dialect_->schema() + " " + prepare_identifier_string(part.schema(), *dialect_); } void query_builder::visit(internal::query_set_part& part) { @@ -358,7 +358,7 @@ void query_builder::visit(internal::query_set_part& part) { if (!first) { query_.sql.append(", "); } - query_.sql.append(dialect_->prepare_identifier_string(column_value.col().column_name()) + "="); + query_.sql.append(prepare_identifier_string(column_value.col().column_name(), *dialect_) + "="); query_.sql.append(determine_value(*dialect_, query_, column_value.expression())); first = false; } @@ -384,7 +384,7 @@ void query_builder::visit(internal::query_set_part& part) { void query_builder::visit(internal::query_drop_sequence_part& part) { query_.command = sql::sql_command::DropSequence; - query_.sql += " " + dialect_->sequence() + " " + dialect_->prepare_identifier_string(part.sequence_name()); + query_.sql += " " + dialect_->sequence() + " " + prepare_identifier_string(part.sequence_name(), *dialect_); } void query_builder::visit(internal::query_drop_table_part& part) { @@ -488,9 +488,9 @@ std::string query_builder::build_table_name(const sql::dialect_token token, cons } std::string query_builder::build_table_name(const sql::dialect& d, const table& t) { - return (!d.default_schema_name().empty() ? d.prepare_identifier_string(d.default_schema_name()) + "." : "") + - d.prepare_identifier_string(t.table_name()) + - (!t.has_alias() ? "" : " " + d.prepare_identifier_string(t.name())); + return (!d.default_schema_name().empty() ? prepare_identifier_string(d.default_schema_name(), d) + "." : "") + + prepare_identifier_string(t.table_name(), d) + + (!t.has_alias() ? "" : " " + prepare_identifier_string(t.name(), d)); } std::string query_builder::build_add_constraint_string(const table_constraint& c) const { diff --git a/source/orm/query/query_utils.cpp b/source/orm/query/query_utils.cpp index 64a24a1..fb40810 100644 --- a/source/orm/query/query_utils.cpp +++ b/source/orm/query/query_utils.cpp @@ -8,6 +8,8 @@ #include "matador/sql/dialect.hpp" #include "matador/sql/query_context.hpp" +#include "matador/utils/value.hpp" + namespace matador::query { void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col) { // Expression-backed select item: () [AS alias] @@ -65,4 +67,50 @@ std::string prepare_criteria(const sql::dialect& d, const table_column& col) { return result; } + +std::string to_query_string(const utils::value &val, const sql::dialect& d) { + if (val.is_null()) { + return "NULL"; + } + if (val.is_string()) { + return d.token_at(sql::dialect_token::BeginStringData) + val.str() + d.token_at(sql::dialect_token::BeginStringData); + } + + return val.str(); +} + +std::string prepare_literal(const std::string &str, const sql::dialect& d) { + std::string result(str); + escape_quotes_in_literals(result, d); + return result; +} + +std::string prepare_identifier_string(const std::string &col, const sql::dialect& d) { + auto parts = utils::split(col, '.'); + + for (auto &part: parts) { + escape_quotes_in_identifier(part, d); + part.insert(0, d.token_at(sql::dialect_token::StartQuote)); + part += d.token_at(sql::dialect_token::EndQuote); + } + + return utils::join(parts, "."); +} + +void escape_quotes_in_identifier(std::string &str, const sql::dialect& d) { + const std::string& open_char(d.token_at(sql::dialect_token::StartQuote)); + const std::string& close_char(d.token_at(sql::dialect_token::EndQuote)); + if (d.identifier_escape_type() == sql::dialect::escape_identifier_t::EscapeClosingBracket) { + utils::replace_all(str, close_char, close_char + close_char); + } else { + utils::replace_all(str, open_char, open_char + open_char); + } +} + +void escape_quotes_in_literals(std::string &str, const sql::dialect& d) { + const std::string& single_quote_char(d.token_at(sql::dialect_token::StringQuote)); + const std::string double_quote(single_quote_char + single_quote_char); + utils::replace_all(str, single_quote_char, double_quote); +} + } \ No newline at end of file diff --git a/source/orm/sql/dialect.cpp b/source/orm/sql/dialect.cpp index 84ab278..0e3a7c1 100644 --- a/source/orm/sql/dialect.cpp +++ b/source/orm/sql/dialect.cpp @@ -22,59 +22,11 @@ const std::string &dialect::to_string(const bool val) const { return bool_strings_[static_cast(val)]; } -std::string dialect::to_sql_string(const utils::value &val) const { - if (val.is_null()) { - return "NULL"; - } - if (val.is_string()) { - return token_at(dialect_token::BeginStringData) + val.str() + token_at(dialect_token::BeginStringData); - } - - return val.str(); -} - void dialect::bool_strings(const std::string &true_string, const std::string &false_string) { bool_strings_[0] = false_string; bool_strings_[1] = true_string; } -std::string dialect::prepare_identifier_string(const std::string &col) const { - auto parts = utils::split(col, '.'); - - for (auto &part: parts) { - escape_quotes_in_identifier(part); - quote_identifier(part); - } - return utils::join(parts, "."); -} - -std::string dialect::prepare_literal(const std::string &str) const { - std::string result(str); - escape_quotes_in_literals(result); - return result; -} - -void dialect::quote_identifier(std::string &str) const { - str.insert(0, token_at(dialect_token::StartQuote)); - str += token_at(dialect_token::EndQuote); -} - -void dialect::escape_quotes_in_identifier(std::string &str) const { - const std::string open_char(token_at(dialect_token::StartQuote)); - const std::string close_char(token_at(dialect_token::EndQuote)); - if (identifier_escape_type() == escape_identifier_t::ESCAPE_CLOSING_BRACKET) { - utils::replace_all(str, close_char, close_char + close_char); - } else { - utils::replace_all(str, open_char, open_char + open_char); - } -} - -void dialect::escape_quotes_in_literals(std::string &str) const { - const std::string single_quote(token_at(dialect_token::StringQuote)); - const std::string double_quote(token_at(dialect_token::StringQuote) + token_at(dialect_token::StringQuote)); - utils::replace_all(str, single_quote, double_quote); -} - dialect::escape_identifier_t dialect::identifier_escape_type() const { return identifier_escape_type_; } diff --git a/todo.md b/todo.md index fe94f84..8af09fe 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,5 @@ # Todo -- move `prepare_*` methods from `dialect` to `query_compiler` - add `update_update_builder` (returning multiple statements) - add `delete_update_builder` (returning multiple statements) - implement polymorphic class hierarchies