Compare commits
4 Commits
caacdfa34a
...
e58b7b9b0d
| Author | SHA1 | Date |
|---|---|---|
|
|
e58b7b9b0d | |
|
|
052ff657c1 | |
|
|
08a28b1627 | |
|
|
60f03246dc |
|
|
@ -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);
|
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_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 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
|
#endif //MATADOR_QUERY_UTILS_HPP
|
||||||
|
|
@ -21,15 +21,14 @@ namespace matador::sql {
|
||||||
|
|
||||||
class connection_impl;
|
class connection_impl;
|
||||||
|
|
||||||
class dialect final
|
class dialect final {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Holding enums concerning escaping identifiers
|
* Holding enums concerning escaping identifiers
|
||||||
*/
|
*/
|
||||||
enum class escape_identifier_t : uint8_t {
|
enum class escape_identifier_t : uint8_t {
|
||||||
ESCAPE_BOTH_SAME, /**< The escape quotes are the same */
|
EscapeBothSame, /**< The escape quotes are the same */
|
||||||
ESCAPE_CLOSING_BRACKET /**< The escape quotes differ; escape the closing one */
|
EscapeClosingBracket /**< The escape quotes differ; escape the closing one */
|
||||||
};
|
};
|
||||||
|
|
||||||
using token_to_string_map = std::unordered_map<dialect_token, std::string>;
|
using token_to_string_map = std::unordered_map<dialect_token, std::string>;
|
||||||
|
|
@ -44,50 +43,10 @@ public:
|
||||||
[[nodiscard]] const std::string& data_type_at(utils::basic_type type) const;
|
[[nodiscard]] const std::string& data_type_at(utils::basic_type type) const;
|
||||||
[[nodiscard]] const std::string& sql_function_at(sql_function_t func) 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]] 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);
|
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
|
* Returns how the identifier quotes should be
|
||||||
* escaped.
|
* escaped.
|
||||||
|
|
@ -192,7 +151,7 @@ private:
|
||||||
next_placeholder_func placeholder_func_ = [](size_t) { return "?"; };
|
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); };
|
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_;
|
std::string default_schema_name_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
#include "matador/query/attribute_string_writer.hpp"
|
#include "matador/query/attribute_string_writer.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/query_utils.hpp"
|
||||||
|
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
#include <matador/utils/convert.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
|
||||||
|
#include <matador/utils/convert.hpp>
|
||||||
#include "matador/utils/string.hpp"
|
#include "matador/utils/string.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
attribute_string_writer::attribute_string_writer(const sql::dialect &d,
|
attribute_string_writer::attribute_string_writer(const sql::dialect &d,
|
||||||
const std::optional<std::reference_wrapper<const
|
const std::optional<std::reference_wrapper<const
|
||||||
sql::connection_impl> > conn)
|
sql::connection_impl> > conn)
|
||||||
: dialect_(d)
|
: dialect_(d)
|
||||||
, conn_(conn) {
|
, conn_(conn) {}
|
||||||
}
|
|
||||||
|
|
||||||
const sql::dialect &attribute_string_writer::dialect() const {
|
const sql::dialect &attribute_string_writer::dialect() const {
|
||||||
return dialect_;
|
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) {
|
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) {
|
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*/) {
|
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) {
|
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*/) {
|
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) {
|
void attribute_string_writer::write_value(size_t /*pos*/, const utils::blob_type_t &x) {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ void criteria_evaluator::visit(const not_criteria &node) {
|
||||||
|
|
||||||
void criteria_evaluator::evaluate_value(const criteria_value &value) {
|
void criteria_evaluator::evaluate_value(const criteria_value &value) {
|
||||||
std::visit(overload{
|
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); }
|
[this](const utils::placeholder&) { clause_ += dialect_.next_placeholder(query_.bind_vars); }
|
||||||
}, value);
|
}, value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ void query_builder::visit(internal::query_alter_part& part) {
|
||||||
void query_builder::visit(internal::query_alter_table_part& part) {
|
void query_builder::visit(internal::query_alter_table_part& part) {
|
||||||
query_.command = sql::sql_command::AlterTable;
|
query_.command = sql::sql_command::AlterTable;
|
||||||
query_.sql += " " + dialect_->token_at(part.token()) + " " +
|
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) {
|
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() + " ";
|
query_.sql += " " + dialect_->group_by() + " ";
|
||||||
if (part.columns().size() < 2) {
|
if (part.columns().size() < 2) {
|
||||||
for (const auto& col : part.columns()) {
|
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 {
|
} else {
|
||||||
auto it = part.columns().begin();
|
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) {
|
for (; it != part.columns().end(); ++it) {
|
||||||
query_.sql.append(", ");
|
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() + " ";
|
query_.sql += " " + dialect_->order_by() + " ";
|
||||||
if (part.columns().size() < 2) {
|
if (part.columns().size() < 2) {
|
||||||
for (const auto& col : part.columns()) {
|
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 {
|
} else {
|
||||||
auto it = part.columns().begin();
|
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) {
|
for (; it != part.columns().end(); ++it) {
|
||||||
query_.sql.append(", ");
|
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) {
|
void query_builder::visit(internal::query_into_part& part) {
|
||||||
query_.table_name = part.table().name();
|
query_.table_name = part.table().name();
|
||||||
query_.sql += " " + dialect_->into() +
|
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_);
|
build_columns_with_name_only(query_.sql, part.columns(), *dialect_);
|
||||||
query_.sql += ")"/* + result*/;
|
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);
|
std::string build_constraint(const table_constraint& cons, const sql::dialect& d);
|
||||||
|
|
||||||
void query_builder::visit(internal::query_create_table_part& part) {
|
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();
|
query_.table_name = part.table().name();
|
||||||
|
|
||||||
finisher_ = [](sql::query_context& ctx) { ctx.sql += ")"; };
|
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) {
|
void query_builder::visit(internal::query_create_sequence_part& part) {
|
||||||
query_.command = sql::sql_command::CreateSequence;
|
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) {
|
void query_builder::visit(internal::query_create_schema_part& part) {
|
||||||
query_.command = sql::sql_command::CreateSchema;
|
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) {
|
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) {
|
void query_builder::visit(internal::query_drop_schema_part& part) {
|
||||||
query_.sql += " " + dialect_->drop() + " " +
|
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) {
|
void query_builder::visit(internal::query_set_part& part) {
|
||||||
|
|
@ -358,7 +358,7 @@ void query_builder::visit(internal::query_set_part& part) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
query_.sql.append(", ");
|
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()));
|
query_.sql.append(determine_value(*dialect_, query_, column_value.expression()));
|
||||||
first = false;
|
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) {
|
void query_builder::visit(internal::query_drop_sequence_part& part) {
|
||||||
query_.command = sql::sql_command::DropSequence;
|
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) {
|
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) {
|
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()) + "." : "") +
|
return (!d.default_schema_name().empty() ? prepare_identifier_string(d.default_schema_name(), d) + "." : "") +
|
||||||
d.prepare_identifier_string(t.table_name()) +
|
prepare_identifier_string(t.table_name(), d) +
|
||||||
(!t.has_alias() ? "" : " " + d.prepare_identifier_string(t.name()));
|
(!t.has_alias() ? "" : " " + prepare_identifier_string(t.name(), d));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string query_builder::build_add_constraint_string(const table_constraint& c) const {
|
std::string query_builder::build_add_constraint_string(const table_constraint& c) const {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
#include "matador/sql/query_context.hpp"
|
#include "matador/sql/query_context.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/value.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col) {
|
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col) {
|
||||||
// Expression-backed select item: (<expr>) [AS alias]
|
// Expression-backed select item: (<expr>) [AS alias]
|
||||||
|
|
@ -65,4 +67,50 @@ std::string prepare_criteria(const sql::dialect& d, const table_column& col) {
|
||||||
|
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -22,59 +22,11 @@ const std::string &dialect::to_string(const bool val) const {
|
||||||
return bool_strings_[static_cast<int>(val)];
|
return bool_strings_[static_cast<int>(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) {
|
void dialect::bool_strings(const std::string &true_string, const std::string &false_string) {
|
||||||
bool_strings_[0] = false_string;
|
bool_strings_[0] = false_string;
|
||||||
bool_strings_[1] = true_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 {
|
dialect::escape_identifier_t dialect::identifier_escape_type() const {
|
||||||
return identifier_escape_type_;
|
return identifier_escape_type_;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#include "models/shipment.hpp"
|
#include "models/shipment.hpp"
|
||||||
#include "models/model_metas.hpp"
|
#include "models/model_metas.hpp"
|
||||||
|
|
||||||
#include "../test/utils/record_printer.hpp"
|
#include "../test/utils/RecordPrinter.hpp"
|
||||||
|
|
||||||
using namespace matador::object;
|
using namespace matador::object;
|
||||||
using namespace matador::query;
|
using namespace matador::query;
|
||||||
|
|
@ -633,7 +633,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation",
|
||||||
|
|
||||||
REQUIRE(pkgs.is_ok());
|
REQUIRE(pkgs.is_ok());
|
||||||
|
|
||||||
record_printer printer(std::cout);
|
RecordPrinter printer(std::cout);
|
||||||
printer.print(*pkgs);
|
printer.print(*pkgs);
|
||||||
|
|
||||||
auto shipment_records = select({SHIPMENT.tracking_number, SHIPMENT.id, PACKAGE.weight, PACKAGE.weight})
|
auto shipment_records = select({SHIPMENT.tracking_number, SHIPMENT.id, PACKAGE.weight, PACKAGE.weight})
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,19 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-m
|
||||||
auto res = ses.insert(r);
|
auto res = ses.insert(r);
|
||||||
REQUIRE(res.is_ok());
|
REQUIRE(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto find_result = ses.find<ingredient>();
|
||||||
|
REQUIRE(find_result.is_ok());
|
||||||
|
auto all_ingredients = find_result.release();
|
||||||
|
std::vector<object_ptr<ingredient>> ingredients_repo;
|
||||||
|
std::vector expected_ingredients_sizes {2, 1, 1, 1, 1, 1, 1};
|
||||||
|
size_t index {0};
|
||||||
|
for (auto it = all_ingredients.begin(); it != all_ingredients.end(); ++it) {
|
||||||
|
REQUIRE(!it->recipes.empty());
|
||||||
|
REQUIRE(it->recipes.size() == expected_ingredients_sizes[index++]);
|
||||||
|
ingredients_repo.emplace_back(it.optr());
|
||||||
|
}
|
||||||
|
REQUIRE(ingredients_repo.size() == ingredients.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-many lazy relation", "[session][find][many-to-many][lazy]") {
|
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-many lazy relation", "[session][find][many-to-many][lazy]") {
|
||||||
|
|
@ -352,4 +365,18 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-m
|
||||||
auto res = ses.insert(r);
|
auto res = ses.insert(r);
|
||||||
REQUIRE(res.is_ok());
|
REQUIRE(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto find_result = ses.find<recipe>();
|
||||||
|
REQUIRE(find_result.is_ok());
|
||||||
|
auto all_recipes = find_result.release();
|
||||||
|
std::vector<object_ptr<recipe>> recipes_repo;
|
||||||
|
std::vector expected_recipe_sizes {3, 2, 3};
|
||||||
|
size_t index {0};
|
||||||
|
for (auto it = all_recipes.begin(); it != all_recipes.end(); ++it) {
|
||||||
|
REQUIRE(!it->ingredients.empty());
|
||||||
|
REQUIRE(it->ingredients.size() == expected_recipe_sizes[index++]);
|
||||||
|
recipes_repo.emplace_back(it.optr());
|
||||||
|
}
|
||||||
|
REQUIRE(recipes_repo.size() == recipes.size());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "record_printer.hpp"
|
#include "RecordPrinter.hpp"
|
||||||
|
|
||||||
#include "matador/utils/basic_types.hpp"
|
#include "matador/utils/basic_types.hpp"
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
namespace matador::test {
|
namespace matador::test {
|
||||||
record_printer::record_printer(std::ostream &os)
|
RecordPrinter::RecordPrinter(std::ostream &os)
|
||||||
: os_(os) {
|
: os_(os) {
|
||||||
type_widths_ = {
|
type_widths_ = {
|
||||||
{utils::basic_type::Int8, 4},
|
{utils::basic_type::Int8, 4},
|
||||||
|
|
@ -32,7 +32,7 @@ record_printer::record_printer(std::ostream &os)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void record_printer::print_header(const sql::record &rec) const {
|
void RecordPrinter::print_header(const sql::record &rec) const {
|
||||||
for (const auto &f: rec.columns()) {
|
for (const auto &f: rec.columns()) {
|
||||||
os_ << std::left << std::setw(width(f)) << f.name() << " ";
|
os_ << std::left << std::setw(width(f)) << f.name() << " ";
|
||||||
}
|
}
|
||||||
|
|
@ -44,14 +44,14 @@ void record_printer::print_header(const sql::record &rec) const {
|
||||||
os_ << "\n";
|
os_ << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void record_printer::print(const sql::record &rec) const {
|
void RecordPrinter::print(const sql::record &rec) const {
|
||||||
for (const auto &f: rec.columns()) {
|
for (const auto &f: rec.columns()) {
|
||||||
os_ << std::left << std::setw(width(f)) << f.str() << " ";
|
os_ << std::left << std::setw(width(f)) << f.str() << " ";
|
||||||
}
|
}
|
||||||
os_ << "\n";
|
os_ << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
int record_printer::width(const sql::field &f) {
|
int RecordPrinter::width(const sql::field &f) {
|
||||||
// If it's a varchar/string and has a defined size, use it if it's reasonable,
|
// If it's a varchar/string and has a defined size, use it if it's reasonable,
|
||||||
// otherwise fall back to type defaults.
|
// otherwise fall back to type defaults.
|
||||||
if ((f.is_varchar() || f.is_string()) && f.size() > 0 && f.size() < 100) {
|
if ((f.is_varchar() || f.is_string()) && f.size() > 0 && f.size() < 100) {
|
||||||
|
|
@ -12,9 +12,9 @@ class record;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace matador::test {
|
namespace matador::test {
|
||||||
class record_printer final {
|
class RecordPrinter final {
|
||||||
public:
|
public:
|
||||||
explicit record_printer(std::ostream &os);
|
explicit RecordPrinter(std::ostream &os);
|
||||||
|
|
||||||
void print_header(const sql::record &rec) const;
|
void print_header(const sql::record &rec) const;
|
||||||
void print(const sql::record &rec) const;
|
void print(const sql::record &rec) const;
|
||||||
51
todo.md
51
todo.md
|
|
@ -1,58 +1,11 @@
|
||||||
# Todo
|
# Todo
|
||||||
|
|
||||||
Order of next steps:
|
- add `update_update_builder` (returning multiple statements)
|
||||||
1. Finish pk generator
|
- add `delete_update_builder` (returning multiple statements)
|
||||||
- classes
|
|
||||||
- detection on schema attach
|
|
||||||
- tests
|
|
||||||
2. PK accessor class
|
|
||||||
- extract from `insert_query_builder` class
|
|
||||||
- get(), set(), is_null/valid/set()
|
|
||||||
- detection on schema attach
|
|
||||||
- tests
|
|
||||||
3. Add owner-awareness to the collection class
|
|
||||||
- add owner field
|
|
||||||
- bind owner on session insert
|
|
||||||
- tests
|
|
||||||
4. Finish `insert_query_builder`
|
|
||||||
- collect all inserts for entities with relations
|
|
||||||
- tests
|
|
||||||
- cascade_type: `Insert`
|
|
||||||
- has_many <-> belongs_to: set root id into the join column of elements
|
|
||||||
- belongs_to <-> has_many: ?
|
|
||||||
- belongs_to <-> has_one: set root id into the foreign object
|
|
||||||
- has_one <-> belongs_to: set root id into the foreign object
|
|
||||||
- has_many_to_many: insert relation table entry
|
|
||||||
|
|
||||||
if generator type `manual` => extract pk before insert
|
|
||||||
=>
|
|
||||||
if generator type `sequence` or `table` => generate pk before insert
|
|
||||||
if generator type `identity` => extract pk after insert and before insert of relation objects
|
|
||||||
|
|
||||||
5. Finish `session::insert` method
|
|
||||||
- use `insert_query_builder`
|
|
||||||
- correct handling of pk generator
|
|
||||||
- tests
|
|
||||||
|
|
||||||
- move `prepare_*` methods from `dialect` to `query_compiler`
|
|
||||||
- add `insert_query_builder` and `update_update_builder` (returning multiple statements)
|
|
||||||
- finish fetch eager many-to-many relations
|
|
||||||
- implement lazy loading
|
|
||||||
- implement polymorphic class hierarchies
|
- implement polymorphic class hierarchies
|
||||||
- finish `database` (schema_repository) class (move add/drop from `session` to `schema`)
|
- finish `database` (schema_repository) class (move add/drop from `session` to `schema`)
|
||||||
- implement a flag class for enumerations
|
- implement a flag class for enumerations
|
||||||
|
|
||||||
- PK generator
|
|
||||||
1. Introduce execute_result
|
|
||||||
2. Add `abstract_pk_generator` class
|
|
||||||
3. Add `identity_pk_generator` class
|
|
||||||
4. Add `sequence_pk_generator` class
|
|
||||||
5. Add `table_pk_generator` class
|
|
||||||
6. Add `manual_pk_generator` class
|
|
||||||
7. Extend `session::insert` logic to use pk generator
|
|
||||||
8. Add generator to `schema_node` or `table` class when attaching type
|
|
||||||
|
|
||||||
|
|
||||||
__Proposal for polymorphic classes:__
|
__Proposal for polymorphic classes:__
|
||||||
|
|
||||||
object_ptr::as<Type> does the following checks;
|
object_ptr::as<Type> does the following checks;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue