diff --git a/README.md b/README.md index 03650d645..850aeb62a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ SQLite ORM light header only library for modern C++. Please read the license pre * **No raw string queries** * **Intuitive syntax** * **Comfortable interface - one code line per single query** -* **Built with modern C++14 features (no macros and external scripts)** +* **Built with modern C++14/C++17/C++20 features (no macros and external scripts)** * **CRUD support** * **Pure select query support** * **Prepared statements support** diff --git a/appveyor.yml b/appveyor.yml index 21f2654b5..bdd03fd7f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -107,7 +107,7 @@ for: install: - |- cd C:\Tools\vcpkg - git fetch --tags && git checkout 2023.11.20 + git fetch --tags && git checkout 2023.12.12 cd %APPVEYOR_BUILD_FOLDER% C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics C:\Tools\vcpkg\vcpkg integrate install @@ -140,7 +140,7 @@ for: install: - |- pushd $HOME/vcpkg - git fetch --tags && git checkout 2023.11.20 + git fetch --tags && git checkout 2023.12.12 popd $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets @@ -168,7 +168,7 @@ for: # using custom vcpkg triplets for building and linking dynamic dependent libraries install: - |- - git clone --depth 1 --branch 2023.11.20 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + git clone --depth 1 --branch 2023.12.12 https://github.com/microsoft/vcpkg.git $HOME/vcpkg $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets vcpkg install sqlite3[core,dbstat,math,json1,fts5] catch2 --overlay-triplets=vcpkg/triplets diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 8f7a91784..5ce4c24b4 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -150,22 +150,12 @@ namespace sqlite_orm { }; template - struct ast_iterator> { + struct ast_iterator, is_binary_operator>>> { using node_type = T; template - void operator()(const node_type& binaryCondition, L& lambda) const { - iterate_ast(binaryCondition.l, lambda); - iterate_ast(binaryCondition.r, lambda); - } - }; - - template - struct ast_iterator> { - using node_type = T; - - template - void operator()(const node_type& node, C& lambda) const { + void operator()(const node_type& node, L& lambda) const { iterate_ast(node.lhs, lambda); iterate_ast(node.rhs, lambda); } diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index f08a744ec..7a7b94b29 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -71,9 +71,9 @@ namespace sqlite_orm { if(columnExpression.empty()) { throw std::system_error{orm_error_code::column_not_found}; } - collectedExpressions.reserve(collectedExpressions.size() + 1); - collectedExpressions.push_back(std::move(columnExpression)); - return collectedExpressions; + this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); + this->collectedExpressions.push_back(std::move(columnExpression)); + return this->collectedExpressions; } template @@ -83,27 +83,27 @@ namespace sqlite_orm { template std::vector& operator()(const asterisk_t& expression, const Ctx& context) { - return collect_table_column_names(collectedExpressions, expression.defined_order, context); + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); } template std::vector& operator()(const object_t& expression, const Ctx& context) { - return collect_table_column_names(collectedExpressions, expression.defined_order, context); + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); } template std::vector& operator()(const columns_t& cols, const Ctx& context) { - collectedExpressions.reserve(collectedExpressions.size() + cols.count); + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); iterate_tuple(cols.columns, [this, &context](auto& colExpr) { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order if(mpl::invoke_t, typename columns_t::columns_type>::value && - collectedExpressions.capacity() > collectedExpressions.size()) { - collectedExpressions.shrink_to_fit(); + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); } - return collectedExpressions; + return this->collectedExpressions; } std::vector collectedExpressions; diff --git a/dev/conditions.h b/dev/conditions.h index 043d6dd25..a06d8ea2b 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -15,6 +15,7 @@ #include "constraints.h" #include "optional_container.h" #include "serializer_context.h" +#include "serialize_result_type.h" #include "tags.h" #include "alias_traits.h" #include "expression.h" @@ -127,12 +128,12 @@ namespace sqlite_orm { using right_type = R; using result_type = Res; - left_type l; - right_type r; + left_type lhs; + right_type rhs; binary_condition() = default; - binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} + binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -142,7 +143,7 @@ namespace sqlite_orm { using is_binary_condition = polyfill::bool_constant>; struct and_condition_string { - operator std::string() const { + serialize_result_type serialize() const { return "AND"; } }; @@ -158,7 +159,7 @@ namespace sqlite_orm { }; struct or_condition_string { - operator std::string() const { + serialize_result_type serialize() const { return "OR"; } }; @@ -174,7 +175,7 @@ namespace sqlite_orm { }; struct is_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "="; } }; @@ -223,7 +224,7 @@ namespace sqlite_orm { }; struct is_not_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "!="; } }; @@ -251,7 +252,7 @@ namespace sqlite_orm { }; struct greater_than_string { - operator std::string() const { + serialize_result_type serialize() const { return ">"; } }; @@ -279,7 +280,7 @@ namespace sqlite_orm { }; struct greater_or_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return ">="; } }; @@ -307,7 +308,7 @@ namespace sqlite_orm { }; struct less_than_string { - operator std::string() const { + serialize_result_type serialize() const { return "<"; } }; @@ -335,7 +336,7 @@ namespace sqlite_orm { }; struct less_or_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "<="; } }; @@ -898,6 +899,8 @@ namespace sqlite_orm { class R, std::enable_if_t, std::is_base_of, + std::is_base_of, + std::is_base_of, is_operator_argument, is_operator_argument>, bool> = true> @@ -909,6 +912,8 @@ namespace sqlite_orm { class R, std::enable_if_t, std::is_base_of, + std::is_base_of, + std::is_base_of, is_operator_argument, is_operator_argument>, bool> = true> diff --git a/dev/expression_object_type.h b/dev/expression_object_type.h index 3e54c1b7e..087602e3b 100644 --- a/dev/expression_object_type.h +++ b/dev/expression_object_type.h @@ -1,8 +1,9 @@ #pragma once -#include // std::decay +#include // std::decay, std::remove_reference #include // std::reference_wrapper +#include "type_traits.h" #include "prepared_statement.h" namespace sqlite_orm { @@ -13,58 +14,35 @@ namespace sqlite_orm { struct expression_object_type; template - struct expression_object_type> : std::decay {}; + using expression_object_type_t = typename expression_object_type::type; - template - struct expression_object_type>> : std::decay {}; + template + using statement_object_type_t = expression_object_type_t>>; template - struct expression_object_type> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type>> : std::decay {}; - - template - struct expression_object_type> { - using type = typename replace_range_t::object_type; - }; + struct expression_object_type, void> : value_unref_type {}; - template - struct expression_object_type, L, O>> { - using type = typename replace_range_t, L, O>::object_type; + template + struct expression_object_type> { + using type = object_type_t; }; template - struct expression_object_type> { - using type = T; - }; - - template - struct expression_object_type, Ids...>> { - using type = T; - }; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type>> : std::decay {}; - - template - struct expression_object_type> { - using type = typename insert_range_t::object_type; - }; - - template - struct expression_object_type, L, O>> { - using type = typename insert_range_t, L, O>::object_type; + struct expression_object_type> { + using type = object_type_t; }; template - struct expression_object_type> : std::decay {}; - - template - struct expression_object_type, Cols...>> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template struct get_ref_t { diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index dbd415d3a..d7e494117 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -1,10 +1,12 @@ #pragma once -#include // std::is_same, std::decay, std::remove_reference +#include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get #include "functional/cxx_universal.h" // ::size_t +#include "functional/cxx_type_traits_polyfill.h" #include "functional/static_magic.h" +#include "type_traits.h" #include "prepared_statement.h" #include "ast_iterator.h" #include "node_tuple.h" @@ -124,14 +126,14 @@ namespace sqlite_orm { template const auto& get(const internal::prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; + using statement_type = polyfill::remove_cvref_t; + using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; using bind_tuple = internal::bindable_filter_t; using result_type = std::tuple_element_t(N), bind_tuple>; const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { - using node_type = std::decay_t; + using node_type = polyfill::remove_cvref_t; if(internal::is_bindable_v) { ++index; } @@ -149,15 +151,15 @@ namespace sqlite_orm { template auto& get(internal::prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; + using statement_type = std::remove_reference_t; + using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; using bind_tuple = internal::bindable_filter_t; using result_type = std::tuple_element_t(N), bind_tuple>; result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { - using node_type = std::decay_t; + using node_type = polyfill::remove_cvref_t; if(internal::is_bindable_v) { ++index; } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 3975c83ac..b12864af5 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -349,13 +349,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if(context.use_parentheses) { - ss << '('; - } ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; - if(context.use_parentheses) { - ss << ')'; - } return ss.str(); } }; @@ -527,26 +521,6 @@ namespace sqlite_orm { } }; - template - struct statement_serializer, void> { - using statement_type = binary_operator; - - template - std::string operator()(const statement_type& statement, const Ctx& context) const { - auto lhs = serialize(statement.lhs, context); - auto rhs = serialize(statement.rhs, context); - std::stringstream ss; - if(context.use_parentheses) { - ss << '('; - } - ss << lhs << " " << statement.serialize() << " " << rhs; - if(context.use_parentheses) { - ss << ')'; - } - return ss.str(); - } - }; - template struct statement_serializer, void> { using statement_type = count_asterisk_t; @@ -765,19 +739,36 @@ namespace sqlite_orm { }; template - struct statement_serializer> { + struct statement_serializer< + T, + std::enable_if_t, is_binary_operator>>> { using statement_type = T; template - std::string operator()(const statement_type& c, const Ctx& context) const { - auto leftString = serialize(c.l, context); - auto rightString = serialize(c.r, context); + std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesizeLeft = is_binary_condition_v> || + is_binary_operator_v>; + constexpr bool parenthesizeRight = is_binary_condition_v> || + is_binary_operator_v>; + std::stringstream ss; - if(context.use_parentheses) { + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeLeft) { ss << "("; } - ss << leftString << " " << static_cast(c) << " " << rightString; - if(context.use_parentheses) { + ss << serialize(statement.lhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeLeft) { + ss << ")"; + } + ss << " " << statement.serialize() << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeRight) { + ss << "("; + } + ss << serialize(statement.rhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeRight) { ss << ")"; } return ss.str(); @@ -1179,8 +1170,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" @@ -1203,8 +1193,7 @@ namespace sqlite_orm { std::string operator()(const statement_type& ins, const Ctx& context) const { constexpr size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; ss << "INSERT INTO " << streaming_identifier(table.name) << " "; @@ -1231,8 +1220,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; @@ -1347,7 +1335,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); using is_without_rowid = typename std::decay_t::is_without_rowid; @@ -1409,12 +1397,15 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; + std::stringstream ss; if(context.use_parentheses) { ss << '('; } - // note: pass `statement` itself - ss << streaming_serialized(get_column_names(statement, context)); + ss << streaming_serialized(get_column_names(statement, subCtx)); if(context.use_parentheses) { ss << ')'; } @@ -1489,8 +1480,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& rep, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; @@ -1509,7 +1499,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); using is_without_rowid = typename std::decay_t::is_without_rowid; @@ -1682,6 +1672,9 @@ namespace sqlite_orm { template std::string operator()(const statement_type& sel, Ctx context) const { context.skip_table_name = false; + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; std::stringstream ss; if(!is_compound_operator_v) { @@ -1693,7 +1686,7 @@ namespace sqlite_orm { if(get_distinct(sel.col)) { ss << static_cast(distinct(0)) << " "; } - ss << streaming_serialized(get_column_names(sel.col, context)); + ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; if(!hasExplicitFrom) { diff --git a/dev/storage.h b/dev/storage.h index 334bed279..724c038f3 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1140,115 +1140,116 @@ namespace sqlite_orm { #endif template - prepared_statement_t> prepare(select_t sel) { - sel.highest_level = true; - return prepare_impl>(std::move(sel)); + prepared_statement_t> prepare(select_t statement) { + statement.highest_level = true; + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_all_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_all_pointer_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_all_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(replace_raw_t ins) { - return prepare_impl>(std::move(ins)); + prepared_statement_t> prepare(replace_raw_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(insert_raw_t ins) { - return prepare_impl>(std::move(ins)); + prepared_statement_t> prepare(insert_raw_t statement) { + return this->prepare_impl(std::move(statement)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_all_optional_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> + prepare(get_all_optional_t statement) { + return this->prepare_impl(std::move(statement)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(update_all_t upd) { - return prepare_impl>(std::move(upd)); + prepared_statement_t> prepare(update_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(remove_all_t rem) { - return prepare_impl>(std::move(rem)); + prepared_statement_t> prepare(remove_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_pointer_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_optional_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_optional_t statement) { + return this->prepare_impl(std::move(statement)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(update_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_updatable_type(); - return prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template prepared_statement_t> prepare(remove_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template prepared_statement_t> prepare(insert_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(replace_t rep) { - using object_type = typename expression_object_type::type; + prepared_statement_t> prepare(replace_t statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(rep)); + return this->prepare_impl(std::move(statement)); } - template - prepared_statement_t> prepare(insert_range_t statement) { - using object_type = typename expression_object_type::type; + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } - template - prepared_statement_t> prepare(replace_range_t statement) { - using object_type = typename expression_object_type::type; + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(insert_explicit ins) { - using object_type = typename expression_object_type::type; + prepared_statement_t> prepare(insert_explicit statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(ins)); + return this->prepare_impl(std::move(statement)); } template @@ -1276,9 +1277,7 @@ namespace sqlite_orm { template int64 execute(const prepared_statement_t>& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -1294,9 +1293,7 @@ namespace sqlite_orm { template, is_replace_range>, bool> = true> void execute(const prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -1335,9 +1332,7 @@ namespace sqlite_orm { template, is_insert_range>, bool> = true> int64 execute(const prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -1389,9 +1384,7 @@ namespace sqlite_orm { template void execute(const prepared_statement_t>& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); auto& table = this->get_table(); diff --git a/dev/type_traits.h b/dev/type_traits.h index 627efc22a..f1c73d9e3 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -1,6 +1,11 @@ #pragma once #include // std::enable_if, std::is_same, std::is_empty +#if __cpp_lib_unwrap_ref >= 201811L +#include // std::reference_wrapper +#else +#include // std::reference_wrapper +#endif #include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" @@ -11,6 +16,15 @@ namespace sqlite_orm { template using is_any_of = polyfill::disjunction...>; + template + struct value_unref_type : polyfill::remove_cvref {}; + + template + struct value_unref_type> : std::remove_const {}; + + template + using value_unref_type_t = typename value_unref_type::type; + // enable_if for types template class Op, class... Args> using match_if = std::enable_if_t::value>; diff --git a/examples/subquery.cpp b/examples/subquery.cpp index 55257515c..db4cd8449 100644 --- a/examples/subquery.cpp +++ b/examples/subquery.cpp @@ -1305,6 +1305,22 @@ int main(int, char**) { << endl; } } + { + // SELECT employee_id, first_name, last_name, salary, (SELECT AVG(salary) FROM employees), salary > (SELECT AVG(salary) FROM employees) + // FROM "employees"; + auto rows = storage.select(columns(&Employee::id, + &Employee::firstName, + &Employee::lastName, + &Employee::salary, + select(avg(&Employee::salary)), + greater_than(&Employee::salary, select(avg(&Employee::salary))))); + cout << "employee_id first_name last_name salary avg_salary salary>avg" << endl; + cout << "----------- ---------- ---------- ---------- ---------- ----------" << endl; + for(auto& row: rows) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) + << '\t' << std::get<4>(row) << '\t' << std::get<5>(row) << endl; + } + } { // SELECT first_name, last_name, department_id // FROM employees diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a0bd1af7c..84e65e037 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -235,6 +235,11 @@ using std::nullptr_t; #pragma once #include // std::enable_if, std::is_same, std::is_empty +#if __cpp_lib_unwrap_ref >= 201811L +#include // std::reference_wrapper +#else +#include // std::reference_wrapper +#endif // #include "functional/cxx_core_features.h" @@ -391,6 +396,15 @@ namespace sqlite_orm { template using is_any_of = polyfill::disjunction...>; + template + struct value_unref_type : polyfill::remove_cvref {}; + + template + struct value_unref_type> : std::remove_const {}; + + template + using value_unref_type_t = typename value_unref_type::type; + // enable_if for types template class Op, class... Args> using match_if = std::enable_if_t::value>; @@ -3057,6 +3071,8 @@ namespace sqlite_orm { } +// #include "serialize_result_type.h" + // #include "tags.h" // #include "alias_traits.h" @@ -3596,12 +3612,12 @@ namespace sqlite_orm { using right_type = R; using result_type = Res; - left_type l; - right_type r; + left_type lhs; + right_type rhs; binary_condition() = default; - binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} + binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -3611,7 +3627,7 @@ namespace sqlite_orm { using is_binary_condition = polyfill::bool_constant>; struct and_condition_string { - operator std::string() const { + serialize_result_type serialize() const { return "AND"; } }; @@ -3627,7 +3643,7 @@ namespace sqlite_orm { }; struct or_condition_string { - operator std::string() const { + serialize_result_type serialize() const { return "OR"; } }; @@ -3643,7 +3659,7 @@ namespace sqlite_orm { }; struct is_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "="; } }; @@ -3692,7 +3708,7 @@ namespace sqlite_orm { }; struct is_not_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "!="; } }; @@ -3720,7 +3736,7 @@ namespace sqlite_orm { }; struct greater_than_string { - operator std::string() const { + serialize_result_type serialize() const { return ">"; } }; @@ -3748,7 +3764,7 @@ namespace sqlite_orm { }; struct greater_or_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return ">="; } }; @@ -3776,7 +3792,7 @@ namespace sqlite_orm { }; struct less_than_string { - operator std::string() const { + serialize_result_type serialize() const { return "<"; } }; @@ -3804,7 +3820,7 @@ namespace sqlite_orm { }; struct less_or_equal_string { - operator std::string() const { + serialize_result_type serialize() const { return "<="; } }; @@ -4367,6 +4383,8 @@ namespace sqlite_orm { class R, std::enable_if_t, std::is_base_of, + std::is_base_of, + std::is_base_of, is_operator_argument, is_operator_argument>, bool> = true> @@ -4378,6 +4396,8 @@ namespace sqlite_orm { class R, std::enable_if_t, std::is_base_of, + std::is_base_of, + std::is_base_of, is_operator_argument, is_operator_argument>, bool> = true> @@ -14319,22 +14339,12 @@ namespace sqlite_orm { }; template - struct ast_iterator> { + struct ast_iterator, is_binary_operator>>> { using node_type = T; template - void operator()(const node_type& binaryCondition, L& lambda) const { - iterate_ast(binaryCondition.l, lambda); - iterate_ast(binaryCondition.r, lambda); - } - }; - - template - struct ast_iterator> { - using node_type = T; - - template - void operator()(const node_type& node, C& lambda) const { + void operator()(const node_type& node, L& lambda) const { iterate_ast(node.lhs, lambda); iterate_ast(node.rhs, lambda); } @@ -17265,9 +17275,11 @@ namespace sqlite_orm { // #include "expression_object_type.h" -#include // std::decay +#include // std::decay, std::remove_reference #include // std::reference_wrapper +// #include "type_traits.h" + // #include "prepared_statement.h" namespace sqlite_orm { @@ -17278,58 +17290,35 @@ namespace sqlite_orm { struct expression_object_type; template - struct expression_object_type> : std::decay {}; + using expression_object_type_t = typename expression_object_type::type; - template - struct expression_object_type>> : std::decay {}; + template + using statement_object_type_t = expression_object_type_t>>; template - struct expression_object_type> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type>> : std::decay {}; - - template - struct expression_object_type> { - using type = typename replace_range_t::object_type; - }; - - template - struct expression_object_type, L, O>> { - using type = typename replace_range_t, L, O>::object_type; - }; + struct expression_object_type, void> : value_unref_type {}; - template - struct expression_object_type> { - using type = T; + template + struct expression_object_type> { + using type = object_type_t; }; template - struct expression_object_type, Ids...>> { - using type = T; - }; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template - struct expression_object_type>> : std::decay {}; - - template - struct expression_object_type> { - using type = typename insert_range_t::object_type; - }; - - template - struct expression_object_type, L, O>> { - using type = typename insert_range_t, L, O>::object_type; + struct expression_object_type> { + using type = object_type_t; }; template - struct expression_object_type> : std::decay {}; - - template - struct expression_object_type, Cols...>> : std::decay {}; + struct expression_object_type, void> : value_unref_type {}; template struct get_ref_t { @@ -17557,9 +17546,9 @@ namespace sqlite_orm { if(columnExpression.empty()) { throw std::system_error{orm_error_code::column_not_found}; } - collectedExpressions.reserve(collectedExpressions.size() + 1); - collectedExpressions.push_back(std::move(columnExpression)); - return collectedExpressions; + this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); + this->collectedExpressions.push_back(std::move(columnExpression)); + return this->collectedExpressions; } template @@ -17569,27 +17558,27 @@ namespace sqlite_orm { template std::vector& operator()(const asterisk_t& expression, const Ctx& context) { - return collect_table_column_names(collectedExpressions, expression.defined_order, context); + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); } template std::vector& operator()(const object_t& expression, const Ctx& context) { - return collect_table_column_names(collectedExpressions, expression.defined_order, context); + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); } template std::vector& operator()(const columns_t& cols, const Ctx& context) { - collectedExpressions.reserve(collectedExpressions.size() + cols.count); + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); iterate_tuple(cols.columns, [this, &context](auto& colExpr) { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order if(mpl::invoke_t, typename columns_t::columns_type>::value && - collectedExpressions.capacity() > collectedExpressions.size()) { - collectedExpressions.shrink_to_fit(); + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); } - return collectedExpressions; + return this->collectedExpressions; } std::vector collectedExpressions; @@ -18209,13 +18198,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if(context.use_parentheses) { - ss << '('; - } ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; - if(context.use_parentheses) { - ss << ')'; - } return ss.str(); } }; @@ -18387,26 +18370,6 @@ namespace sqlite_orm { } }; - template - struct statement_serializer, void> { - using statement_type = binary_operator; - - template - std::string operator()(const statement_type& statement, const Ctx& context) const { - auto lhs = serialize(statement.lhs, context); - auto rhs = serialize(statement.rhs, context); - std::stringstream ss; - if(context.use_parentheses) { - ss << '('; - } - ss << lhs << " " << statement.serialize() << " " << rhs; - if(context.use_parentheses) { - ss << ')'; - } - return ss.str(); - } - }; - template struct statement_serializer, void> { using statement_type = count_asterisk_t; @@ -18625,19 +18588,36 @@ namespace sqlite_orm { }; template - struct statement_serializer> { + struct statement_serializer< + T, + std::enable_if_t, is_binary_operator>>> { using statement_type = T; template - std::string operator()(const statement_type& c, const Ctx& context) const { - auto leftString = serialize(c.l, context); - auto rightString = serialize(c.r, context); + std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesizeLeft = is_binary_condition_v> || + is_binary_operator_v>; + constexpr bool parenthesizeRight = is_binary_condition_v> || + is_binary_operator_v>; + std::stringstream ss; - if(context.use_parentheses) { + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeLeft) { ss << "("; } - ss << leftString << " " << static_cast(c) << " " << rightString; - if(context.use_parentheses) { + ss << serialize(statement.lhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeLeft) { + ss << ")"; + } + ss << " " << statement.serialize() << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeRight) { + ss << "("; + } + ss << serialize(statement.rhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesizeRight) { ss << ")"; } return ss.str(); @@ -19039,8 +19019,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" @@ -19063,8 +19042,7 @@ namespace sqlite_orm { std::string operator()(const statement_type& ins, const Ctx& context) const { constexpr size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; ss << "INSERT INTO " << streaming_identifier(table.name) << " "; @@ -19091,8 +19069,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; @@ -19207,7 +19184,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); using is_without_rowid = typename std::decay_t::is_without_rowid; @@ -19269,12 +19246,15 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; + std::stringstream ss; if(context.use_parentheses) { ss << '('; } - // note: pass `statement` itself - ss << streaming_serialized(get_column_names(statement, context)); + ss << streaming_serialized(get_column_names(statement, subCtx)); if(context.use_parentheses) { ss << ')'; } @@ -19349,8 +19329,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& rep, const Ctx& context) const { - using expression_type = std::decay_t; - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); std::stringstream ss; @@ -19369,7 +19348,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; auto& table = pick_table(context.db_objects); using is_without_rowid = typename std::decay_t::is_without_rowid; @@ -19542,6 +19521,9 @@ namespace sqlite_orm { template std::string operator()(const statement_type& sel, Ctx context) const { context.skip_table_name = false; + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; std::stringstream ss; if(!is_compound_operator_v) { @@ -19553,7 +19535,7 @@ namespace sqlite_orm { if(get_distinct(sel.col)) { ss << static_cast(distinct(0)) << " "; } - ss << streaming_serialized(get_column_names(sel.col, context)); + ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; if(!hasExplicitFrom) { @@ -21646,115 +21628,116 @@ namespace sqlite_orm { #endif template - prepared_statement_t> prepare(select_t sel) { - sel.highest_level = true; - return prepare_impl>(std::move(sel)); + prepared_statement_t> prepare(select_t statement) { + statement.highest_level = true; + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_all_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_all_pointer_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_all_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(replace_raw_t ins) { - return prepare_impl>(std::move(ins)); + prepared_statement_t> prepare(replace_raw_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(insert_raw_t ins) { - return prepare_impl>(std::move(ins)); + prepared_statement_t> prepare(insert_raw_t statement) { + return this->prepare_impl(std::move(statement)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_all_optional_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> + prepare(get_all_optional_t statement) { + return this->prepare_impl(std::move(statement)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(update_all_t upd) { - return prepare_impl>(std::move(upd)); + prepared_statement_t> prepare(update_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(remove_all_t rem) { - return prepare_impl>(std::move(rem)); + prepared_statement_t> prepare(remove_all_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_t statement) { + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(get_pointer_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_optional_t get_) { - return prepare_impl>(std::move(get_)); + prepared_statement_t> prepare(get_optional_t statement) { + return this->prepare_impl(std::move(statement)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(update_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_updatable_type(); - return prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template prepared_statement_t> prepare(remove_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template prepared_statement_t> prepare(insert_t statement) { - using object_type = typename expression_object_type::type; + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(replace_t rep) { - using object_type = typename expression_object_type::type; + prepared_statement_t> prepare(replace_t statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(rep)); + return this->prepare_impl(std::move(statement)); } - template - prepared_statement_t> prepare(insert_range_t statement) { - using object_type = typename expression_object_type::type; + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } - template - prepared_statement_t> prepare(replace_range_t statement) { - using object_type = typename expression_object_type::type; + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(statement)); + return this->prepare_impl(std::move(statement)); } template - prepared_statement_t> prepare(insert_explicit ins) { - using object_type = typename expression_object_type::type; + prepared_statement_t> prepare(insert_explicit statement) { + using object_type = expression_object_type_t; this->assert_mapped_type(); - return this->prepare_impl>(std::move(ins)); + return this->prepare_impl(std::move(statement)); } template @@ -21782,9 +21765,7 @@ namespace sqlite_orm { template int64 execute(const prepared_statement_t>& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -21800,9 +21781,7 @@ namespace sqlite_orm { template, is_replace_range>, bool> = true> void execute(const prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -21841,9 +21820,7 @@ namespace sqlite_orm { template, is_insert_range>, bool> = true> int64 execute(const prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); @@ -21895,9 +21872,7 @@ namespace sqlite_orm { template void execute(const prepared_statement_t>& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; + using object_type = statement_object_type_t; sqlite3_stmt* stmt = reset_stmt(statement.stmt); auto& table = this->get_table(); @@ -22135,13 +22110,17 @@ namespace sqlite_orm { } #pragma once -#include // std::is_same, std::decay, std::remove_reference +#include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get // #include "functional/cxx_universal.h" // ::size_t +// #include "functional/cxx_type_traits_polyfill.h" + // #include "functional/static_magic.h" +// #include "type_traits.h" + // #include "prepared_statement.h" // #include "ast_iterator.h" @@ -22525,14 +22504,14 @@ namespace sqlite_orm { template const auto& get(const internal::prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; + using statement_type = polyfill::remove_cvref_t; + using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; using bind_tuple = internal::bindable_filter_t; using result_type = std::tuple_element_t(N), bind_tuple>; const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { - using node_type = std::decay_t; + using node_type = polyfill::remove_cvref_t; if(internal::is_bindable_v) { ++index; } @@ -22550,15 +22529,15 @@ namespace sqlite_orm { template auto& get(internal::prepared_statement_t& statement) { - using statement_type = std::decay_t; - using expression_type = typename statement_type::expression_type; + using statement_type = std::remove_reference_t; + using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; using bind_tuple = internal::bindable_filter_t; using result_type = std::tuple_element_t(N), bind_tuple>; result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { - using node_type = std::decay_t; + using node_type = polyfill::remove_cvref_t; if(internal::is_bindable_v) { ++index; } diff --git a/tests/statement_serializer_tests/aggregate_functions.cpp b/tests/statement_serializer_tests/aggregate_functions.cpp index c255cb408..667b93187 100644 --- a/tests/statement_serializer_tests/aggregate_functions.cpp +++ b/tests/statement_serializer_tests/aggregate_functions.cpp @@ -24,7 +24,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = avg(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((AVG("id")))"; + expected = R"(AVG("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -36,7 +36,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = avg(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((AVG("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(AVG("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -52,7 +52,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = count(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((COUNT("id")))"; + expected = R"(COUNT("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -64,7 +64,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = count(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((COUNT("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(COUNT("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -84,7 +84,7 @@ TEST_CASE("statement_serializer aggregate functions") { SECTION("with filter") { auto expression = count().filter(where(less_than(&User::id, 10))); value = serialize(expression, context); - expected = R"(COUNT(*) FILTER (WHERE ("id" < 10)))"; + expected = R"(COUNT(*) FILTER (WHERE "id" < 10))"; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("with table reference") { @@ -111,7 +111,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = group_concat(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((GROUP_CONCAT("id")))"; + expected = R"(GROUP_CONCAT("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -123,7 +123,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = group_concat(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((GROUP_CONCAT("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(GROUP_CONCAT("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -139,7 +139,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = group_concat(&User::id, "-"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((GROUP_CONCAT("id", '-')))"; + expected = R"(GROUP_CONCAT("id", '-'))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -151,7 +151,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = group_concat(&User::id, "-").filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((GROUP_CONCAT("id", '-')) FILTER (WHERE ("id" < 10)))"; + expected = R"(GROUP_CONCAT("id", '-') FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -167,7 +167,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = max(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((MAX("id")))"; + expected = R"(MAX("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -179,7 +179,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = max(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((MAX("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(MAX("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -195,7 +195,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = min(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((MIN("id")))"; + expected = R"(MIN("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -207,7 +207,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = min(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((MIN("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(MIN("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -223,7 +223,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = sum(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((SUM("id")))"; + expected = R"(SUM("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -235,7 +235,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = sum(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((SUM("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(SUM("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -251,7 +251,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = total(&User::id); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((TOTAL("id")))"; + expected = R"(TOTAL("id"))"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -263,7 +263,7 @@ TEST_CASE("statement_serializer aggregate functions") { auto expression = total(&User::id).filter(where(less_than(&User::id, 10))); SECTION("use_parentheses") { context.use_parentheses = true; - expected = R"((TOTAL("id")) FILTER (WHERE ("id" < 10)))"; + expected = R"(TOTAL("id") FILTER (WHERE "id" < 10))"; } SECTION("!use_parentheses") { context.use_parentheses = false; diff --git a/tests/statement_serializer_tests/arithmetic_operators.cpp b/tests/statement_serializer_tests/arithmetic_operators.cpp index 29248eb54..764b062b4 100644 --- a/tests/statement_serializer_tests/arithmetic_operators.cpp +++ b/tests/statement_serializer_tests/arithmetic_operators.cpp @@ -15,7 +15,7 @@ TEST_CASE("statement_serializer arithmetic operators") { SECTION("operator") { value = serialize(c(3) + 5, context); } - expected = "(3 + 5)"; + expected = "3 + 5"; } SECTION("sub") { SECTION("func") { @@ -24,7 +24,7 @@ TEST_CASE("statement_serializer arithmetic operators") { SECTION("operator") { value = serialize(c(5) - -9, context); } - expected = "(5 - -9)"; + expected = "5 - -9"; } SECTION("mul") { SECTION("func") { @@ -33,7 +33,7 @@ TEST_CASE("statement_serializer arithmetic operators") { SECTION("operator") { value = serialize(c(10) * 0.5, context); } - expected = "(10 * 0.5)"; + expected = "10 * 0.5"; } SECTION("div") { SECTION("func") { @@ -42,7 +42,7 @@ TEST_CASE("statement_serializer arithmetic operators") { SECTION("operator") { value = serialize(c(10) / 2, context); } - expected = "(10 / 2)"; + expected = "10 / 2"; } SECTION("mod") { SECTION("func") { @@ -51,7 +51,21 @@ TEST_CASE("statement_serializer arithmetic operators") { SECTION("operator") { value = serialize(c(20) % 3, context); } - expected = "(20 % 3)"; + expected = "20 % 3"; + } + SECTION("parentheses keeping order of precedence") { + SECTION("1") { + value = serialize(c(4) + 5 + 3, context); + expected = "(4 + 5) + 3"; + } + SECTION("2") { + value = serialize(4 + (c(5) + 3), context); + expected = "4 + (5 + 3)"; + } + SECTION("3") { + value = serialize(4 + c(5) * 3 + 1, context); + expected = "(4 + (5 * 3)) + 1"; + } } REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/column_constraints/check.cpp b/tests/statement_serializer_tests/column_constraints/check.cpp index 3fc6c26bd..7466550c0 100644 --- a/tests/statement_serializer_tests/column_constraints/check.cpp +++ b/tests/statement_serializer_tests/column_constraints/check.cpp @@ -25,12 +25,12 @@ TEST_CASE("statement_serializer check") { context_t context{dbObjects}; std::string value; std::string expected; - SECTION("with parentheses") { + SECTION("use_parentheses") { context.use_parentheses = true; value = serialize(ch, context); - expected = R"(CHECK (("col3" > 0)))"; + expected = R"(CHECK ("col3" > 0))"; } - SECTION("without parentheses") { + SECTION("!use_parentheses") { context.use_parentheses = false; value = serialize(ch, context); expected = R"(CHECK ("col3" > 0))"; @@ -59,12 +59,12 @@ TEST_CASE("statement_serializer check") { context_t context{dbObjects}; std::string value; std::string expected; - SECTION("with parentheses") { + SECTION("use_parentheses") { context.use_parentheses = true; value = serialize(ch, context); - expected = R"(CHECK ((0 < "PRICE")))"; + expected = R"(CHECK (0 < "PRICE"))"; } - SECTION("without parentheses") { + SECTION("!use_parentheses") { context.use_parentheses = false; value = serialize(ch, context); expected = R"(CHECK (0 < "PRICE"))"; diff --git a/tests/statement_serializer_tests/column_constraints/default.cpp b/tests/statement_serializer_tests/column_constraints/default.cpp index 0b78cbdfe..75d5d5cac 100644 --- a/tests/statement_serializer_tests/column_constraints/default.cpp +++ b/tests/statement_serializer_tests/column_constraints/default.cpp @@ -22,7 +22,7 @@ TEST_CASE("statement_serializer default") { auto def = default_value(datetime("now")); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "DEFAULT ((DATETIME('now')))"; + expected = "DEFAULT (DATETIME('now'))"; } SECTION("!use_parentheses") { context.use_parentheses = false; diff --git a/tests/statement_serializer_tests/column_constraints/generated.cpp b/tests/statement_serializer_tests/column_constraints/generated.cpp index 3bd14d62b..2b6d00480 100644 --- a/tests/statement_serializer_tests/column_constraints/generated.cpp +++ b/tests/statement_serializer_tests/column_constraints/generated.cpp @@ -44,37 +44,37 @@ TEST_CASE("statement_serializer generated") { SECTION("full") { auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)); value = serialize(constraint, context); - expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))))"; + expected = R"(GENERATED ALWAYS AS ("a" * ABS("b")))"; } SECTION("full virtual") { auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)).virtual_(); value = serialize(constraint, context); - expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))) VIRTUAL)"; + expected = R"(GENERATED ALWAYS AS ("a" * ABS("b")) VIRTUAL)"; } SECTION("full stored") { auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)).stored(); value = serialize(constraint, context); - expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))) STORED)"; + expected = R"(GENERATED ALWAYS AS ("a" * ABS("b")) STORED)"; } SECTION("not full") { auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)); value = serialize(constraint, context); - expected = R"(AS (("a" * (ABS("b")))))"; + expected = R"(AS ("a" * ABS("b")))"; } SECTION("not full virtual") { auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)).virtual_(); value = serialize(constraint, context); - expected = R"(AS (("a" * (ABS("b")))) VIRTUAL)"; + expected = R"(AS ("a" * ABS("b")) VIRTUAL)"; } SECTION("not full stored") { auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)).stored(); value = serialize(constraint, context); - expected = R"(AS (("a" * (ABS("b")))) STORED)"; + expected = R"(AS ("a" * ABS("b")) STORED)"; } SECTION("length") { auto constraint = generated_always_as(length(&Type::a)); value = serialize(constraint, context); - expected = R"(GENERATED ALWAYS AS ((LENGTH("a"))))"; + expected = R"(GENERATED ALWAYS AS (LENGTH("a")))"; } REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/column_names.cpp b/tests/statement_serializer_tests/column_names.cpp index 6211a70b4..ce0844f4e 100644 --- a/tests/statement_serializer_tests/column_names.cpp +++ b/tests/statement_serializer_tests/column_names.cpp @@ -116,6 +116,14 @@ TEST_CASE("statement_serializer column names") { REQUIRE(value == expected); } } + SECTION("subquery") { + internal::db_objects_tuple<> dbObjects; + using context_t = internal::serializer_context>; + context_t context{dbObjects}; + context.use_parentheses = false; + std::string value = serialize(select(select(1)), context); + REQUIRE(value == "SELECT (SELECT 1)"); + } // note: here we test whether the serializer serializes the correct column // even if the object in question isn't the first in the table definition SECTION("by explicit column pointer") { diff --git a/tests/statement_serializer_tests/comparison_operators.cpp b/tests/statement_serializer_tests/comparison_operators.cpp index af53eec0c..c23d24843 100644 --- a/tests/statement_serializer_tests/comparison_operators.cpp +++ b/tests/statement_serializer_tests/comparison_operators.cpp @@ -25,7 +25,7 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c(4) < 5, context); } - expected = "(4 < 5)"; + expected = "4 < 5"; } SECTION("less_or_equal") { SECTION("func") { @@ -37,7 +37,7 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c(10) <= 15, context); } - expected = "(10 <= 15)"; + expected = "10 <= 15"; } SECTION("greater_than") { SECTION("func") { @@ -49,7 +49,7 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c(1) > 0.5, context); } - expected = "(1 > 0.5)"; + expected = "1 > 0.5"; } SECTION("greater_or_equal") { SECTION("func") { @@ -61,7 +61,7 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c(10) >= -5, context); } - expected = "(10 >= -5)"; + expected = "10 >= -5"; } SECTION("is_equal") { SECTION("func") { @@ -73,7 +73,7 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c("ototo") == "Hey", context); } - expected = "('ototo' = 'Hey')"; + expected = "'ototo' = 'Hey'"; } SECTION("is_not_equal") { SECTION("func") { @@ -85,11 +85,26 @@ TEST_CASE("statement_serializer comparison operators") { SECTION("operator") { value = serialize(c("lala") != 7, context); } - expected = "('lala' != 7)"; + expected = "'lala' != 7"; } SECTION("is_equal_with_table_t") { value = serialize(is_equal("Tom Gregory"), context); - expected = "\"users\" = 'Tom Gregory'"; + expected = R"("users" = 'Tom Gregory')"; + } + SECTION("subquery") { + context.use_parentheses = false; + value = serialize(greater_than(&User::id, select(avg(&User::id))), context); + expected = R"("id" > (SELECT AVG("users"."id") FROM "users"))"; + } + SECTION("parentheses keeping order of precedence") { + SECTION("1") { + value = serialize(true == c(5) > 3, context); + expected = "1 = (5 > 3)"; + } + SECTION("2") { + value = serialize(c(5) > 3 == true, context); + expected = "(5 > 3) = 1"; + } } REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/core_functions.cpp b/tests/statement_serializer_tests/core_functions.cpp index 85291e33c..4dd49bbdc 100644 --- a/tests/statement_serializer_tests/core_functions.cpp +++ b/tests/statement_serializer_tests/core_functions.cpp @@ -24,7 +24,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = length("hi"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(LENGTH('hi'))"; + expected = "LENGTH('hi')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -36,7 +36,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = sqlite_orm::abs(-100); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(ABS(-100))"; + expected = "ABS(-100)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -48,7 +48,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = lower("dancefloor"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(LOWER('dancefloor'))"; + expected = "LOWER('dancefloor')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -60,7 +60,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = upper("call"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(UPPER('call'))"; + expected = "UPPER('call')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -72,7 +72,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = total_changes(); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(TOTAL_CHANGES())"; + expected = "TOTAL_CHANGES()"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -84,7 +84,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = changes(); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(CHANGES())"; + expected = "CHANGES()"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -96,7 +96,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = trim("hey"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(TRIM('hey'))"; + expected = "TRIM('hey')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -108,7 +108,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = trim("hey", "h"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(TRIM('hey', 'h'))"; + expected = "TRIM('hey', 'h')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -120,7 +120,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = ltrim("hey"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(LTRIM('hey'))"; + expected = "LTRIM('hey')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -132,7 +132,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = ltrim("hey", "h"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(LTRIM('hey', 'h'))"; + expected = "LTRIM('hey', 'h')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -144,7 +144,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = rtrim("hey"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(RTRIM('hey'))"; + expected = "RTRIM('hey')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -156,7 +156,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = rtrim("hey", "h"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(RTRIM('hey', 'h'))"; + expected = "RTRIM('hey', 'h')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -168,7 +168,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = hex("love"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(HEX('love'))"; + expected = "HEX('love')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -180,8 +180,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = quote("one"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(QUOTE('one'))"; - ; + expected = "QUOTE('one')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -193,7 +192,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = randomblob(5); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(RANDOMBLOB(5))"; + expected = "RANDOMBLOB(5)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -205,7 +204,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = instr("hi", "i"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(INSTR('hi', 'i'))"; + expected = "INSTR('hi', 'i')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -217,7 +216,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = replace("contigo", "o", "a"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(REPLACE('contigo', 'o', 'a'))"; + expected = "REPLACE('contigo', 'o', 'a')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -229,7 +228,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = sqlite_orm::round(10.5); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(ROUND(10.5))"; + expected = "ROUND(10.5)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -241,7 +240,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = sqlite_orm::round(10.5, 0.5); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(ROUND(10.5, 0.5))"; + expected = "ROUND(10.5, 0.5)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -254,7 +253,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = char_(40, 45); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(CHAR(40, 45))"; + expected = "CHAR(40, 45)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -266,7 +265,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = sqlite_orm::random(); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(RANDOM())"; + expected = "RANDOM()"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -279,7 +278,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = coalesce(10, 15); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(COALESCE(10, 15))"; + expected = "COALESCE(10, 15)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -291,7 +290,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = date("now"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(DATE('now'))"; + expected = "DATE('now')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -303,7 +302,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = time("12:00", "localtime"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(TIME('12:00', 'localtime'))"; + expected = "TIME('12:00', 'localtime')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -315,7 +314,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = datetime("now"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(DATETIME('now'))"; + expected = "DATETIME('now')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -327,7 +326,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = julianday("now"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(JULIANDAY('now'))"; + expected = "JULIANDAY('now')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -339,7 +338,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = strftime("%s", "2014-10-07 02:34:56"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(STRFTIME('%s', '2014-10-07 02:34:56'))"; + expected = "STRFTIME('%s', '2014-10-07 02:34:56')"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -351,7 +350,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = zeroblob(5); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(ZEROBLOB(5))"; + expected = "ZEROBLOB(5)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -363,7 +362,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = substr("Zara", 2); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(SUBSTR('Zara', 2))"; + expected = "SUBSTR('Zara', 2)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -375,7 +374,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = substr("Natasha", 3, 2); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(SUBSTR('Natasha', 3, 2))"; + expected = "SUBSTR('Natasha', 3, 2)"; } SECTION("!use_parentheses") { context.use_parentheses = false; @@ -388,7 +387,7 @@ TEST_CASE("statement_serializer core functions") { auto expression = soundex("Vaso"); SECTION("use_parentheses") { context.use_parentheses = true; - expected = "(SOUNDEX('Vaso'))"; + expected = "SOUNDEX('Vaso')"; } SECTION("!use_parentheses") { context.use_parentheses = false; diff --git a/tests/statement_serializer_tests/logical_operators.cpp b/tests/statement_serializer_tests/logical_operators.cpp index c62e4ddd8..312f4c292 100644 --- a/tests/statement_serializer_tests/logical_operators.cpp +++ b/tests/statement_serializer_tests/logical_operators.cpp @@ -31,7 +31,7 @@ TEST_CASE("statement_serializer logical operators") { SECTION("member function") { stringValue = internal::serialize(c(0).and_(0), context); } - expected = "(0 AND 0)"; + expected = "0 AND 0"; } SECTION("complex") { SECTION("operators") { @@ -40,7 +40,7 @@ TEST_CASE("statement_serializer logical operators") { SECTION("functions") { stringValue = internal::serialize(is_equal(&User::id, 5) and is_equal(&User::name, "Ariana"), context); } - expected = R"((("id" = 5) AND ("name" = 'Ariana')))"; + expected = R"(("id" = 5) AND ("name" = 'Ariana'))"; } } SECTION("or") { @@ -51,7 +51,7 @@ TEST_CASE("statement_serializer logical operators") { SECTION("member function") { stringValue = internal::serialize(c(0).or_(0), context); } - expected = "(0 OR 0)"; + expected = "0 OR 0"; } SECTION("complex") { SECTION("operators") { @@ -60,7 +60,7 @@ TEST_CASE("statement_serializer logical operators") { SECTION("functions") { stringValue = internal::serialize(is_equal(&User::id, 5) or is_equal(&User::name, "Ariana"), context); } - expected = R"((("id" = 5) OR ("name" = 'Ariana')))"; + expected = R"(("id" = 5) OR ("name" = 'Ariana'))"; } } SECTION("in") { @@ -97,5 +97,19 @@ TEST_CASE("statement_serializer logical operators") { expected = R"("id" NOT IN (1, 2, 3))"; } } + SECTION("parentheses keeping order of precedence") { + SECTION("1") { + stringValue = serialize(c(0) and 0, context); + expected = "0 AND 0"; + } + SECTION("2") { + stringValue = serialize(c(0) and 0 or 1, context); + expected = "(0 AND 0) OR 1"; + } + SECTION("3") { + stringValue = serialize(c(0) and 0 or c(1) and 1, context); + expected = "(0 AND 0) OR (1 AND 1)"; + } + } REQUIRE(stringValue == expected); } diff --git a/tests/statement_serializer_tests/statements/remove_all.cpp b/tests/statement_serializer_tests/statements/remove_all.cpp index aeb2ba408..1bfd6c3c8 100644 --- a/tests/statement_serializer_tests/statements/remove_all.cpp +++ b/tests/statement_serializer_tests/statements/remove_all.cpp @@ -26,12 +26,12 @@ TEST_CASE("statement_serializer remove_all") { SECTION("where") { auto statement = remove_all(where(&User::id == c(1))); value = serialize(statement, context); - expected = R"(DELETE FROM "users" WHERE (("id" = 1)))"; + expected = R"(DELETE FROM "users" WHERE ("id" = 1))"; } SECTION("conditions") { auto statement = remove_all(where(&User::id == c(1)), limit(1)); value = serialize(statement, context); - expected = R"(DELETE FROM "users" WHERE (("id" = 1)) LIMIT 1)"; + expected = R"(DELETE FROM "users" WHERE ("id" = 1) LIMIT 1)"; } REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/statements/select.cpp b/tests/statement_serializer_tests/statements/select.cpp index c2d8e23b5..0dd001f38 100644 --- a/tests/statement_serializer_tests/statements/select.cpp +++ b/tests/statement_serializer_tests/statements/select.cpp @@ -48,12 +48,12 @@ TEST_CASE("statement_serializer select_t") { SECTION("!highest_level") { statement.highest_level = false; stringValue = serialize(statement, context); - expected = "(SELECT ((1, 2, 3) = (4, 5, 6)))"; + expected = "(SELECT (1, 2, 3) = (4, 5, 6))"; } SECTION("highest_level") { statement.highest_level = true; stringValue = serialize(statement, context); - expected = "SELECT ((1, 2, 3) = (4, 5, 6))"; + expected = "SELECT (1, 2, 3) = (4, 5, 6)"; } } SECTION("compound operators") { @@ -212,7 +212,7 @@ TEST_CASE("statement_serializer select_t") { context.skip_table_name = false; stringValue = serialize(expression, context); expected = - R"(SELECT "d".* FROM "Dept" "d" LEFT JOIN "Emp" "e" ON ("d"."deptno" = "e"."deptno") WHERE ("e"."deptno" IS NULL))"; + R"(SELECT "d".* FROM "Dept" "d" LEFT JOIN "Emp" "e" ON "d"."deptno" = "e"."deptno" WHERE ("e"."deptno" IS NULL))"; } } } diff --git a/tests/statement_serializer_tests/statements/update.cpp b/tests/statement_serializer_tests/statements/update.cpp index 06f61644a..d76c3622d 100644 --- a/tests/statement_serializer_tests/statements/update.cpp +++ b/tests/statement_serializer_tests/statements/update.cpp @@ -48,11 +48,11 @@ TEST_CASE("statement_serializer update") { auto expression = update_all(set(assign(&A::value, 5)), where(is_equal(&A::address, 1))); SECTION("with question marks") { context.replace_bindable_with_question = true; - expected = R"(UPDATE "table" SET "value" = ? WHERE (("address" = ?)))"; + expected = R"(UPDATE "table" SET "value" = ? WHERE ("address" = ?))"; } SECTION("without question marks") { context.replace_bindable_with_question = false; - expected = R"(UPDATE "table" SET "value" = 5 WHERE (("address" = 1)))"; + expected = R"(UPDATE "table" SET "value" = 5 WHERE ("address" = 1))"; } value = serialize(expression, context); } @@ -62,7 +62,7 @@ TEST_CASE("statement_serializer update") { dynamicSet.push_back(assign(&A::value, 5)); auto expression = update_all(dynamicSet, where(is_equal(&A::address, 1))); context.replace_bindable_with_question = false; - expected = R"(UPDATE "table" SET "value" = 5 WHERE (("address" = 1)))"; + expected = R"(UPDATE "table" SET "value" = 5 WHERE ("address" = 1))"; value = serialize(expression, context); } } diff --git a/tests/statement_serializer_tests/statements/update_all.cpp b/tests/statement_serializer_tests/statements/update_all.cpp index 1f51b1496..4110a1db4 100644 --- a/tests/statement_serializer_tests/statements/update_all.cpp +++ b/tests/statement_serializer_tests/statements/update_all.cpp @@ -55,6 +55,6 @@ TEST_CASE("statement_serializer update_all") { update_all(set(c(&Contact::phone) = select(&Customer::phone, from(), where(c(&Customer::id) == 1)))); auto value = serialize(statement, context); decltype(value) expected = - R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE (("customers"."CustomerId" = 1))))"; + R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)))"; REQUIRE(value == expected); } diff --git a/tests/static_tests/operators_adl.cpp b/tests/static_tests/operators_adl.cpp index 4f712ad93..215be104f 100644 --- a/tests/static_tests/operators_adl.cpp +++ b/tests/static_tests/operators_adl.cpp @@ -1,147 +1,151 @@ -#include -#include - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -using namespace sqlite_orm::literals; -#endif -using sqlite_orm::alias_a; -using sqlite_orm::alias_column; -using sqlite_orm::and_; -using sqlite_orm::c; -using sqlite_orm::colalias_a; -using sqlite_orm::column; -using sqlite_orm::func; -using sqlite_orm::get; -using sqlite_orm::or_; -using sqlite_orm::internal::and_condition_t; -using sqlite_orm::internal::binary_operator; -using sqlite_orm::internal::greater_or_equal_t; -using sqlite_orm::internal::greater_than_t; -using sqlite_orm::internal::is_equal_t; -using sqlite_orm::internal::is_not_equal_t; -using sqlite_orm::internal::less_or_equal_t; -using sqlite_orm::internal::less_than_t; -using sqlite_orm::internal::negated_condition_t; -using sqlite_orm::internal::or_condition_t; -using sqlite_orm::polyfill::is_specialization_of_v; - -template -void runTests(E expression) { - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v 42), greater_than_t>); - STATIC_REQUIRE(is_specialization_of_v expression), greater_than_t>); - STATIC_REQUIRE(is_specialization_of_v expression), greater_than_t>); - - STATIC_REQUIRE(is_specialization_of_v= 42), greater_or_equal_t>); - STATIC_REQUIRE(is_specialization_of_v= expression), greater_or_equal_t>); - STATIC_REQUIRE(is_specialization_of_v= expression), greater_or_equal_t>); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); - - // conc_t + condition_t yield or_condition_t - STATIC_REQUIRE(is_specialization_of_v); - STATIC_REQUIRE(is_specialization_of_v); -} - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -TEST_CASE("inline namespace literals expressions") { - constexpr auto u_alias_builder = "u"_alias; - constexpr auto c_alias = "c"_col; - constexpr auto f_scalar_builder = "f"_scalar; - constexpr auto numeric_cte_alias_builder = 1_ctealias; - constexpr auto cte_alias_builder = "1"_cte; -} - -TEST_CASE("ADL and pointer-to-member expressions") { - struct User { - int id; - }; - constexpr auto user_table = c(); - constexpr auto u_alias = "u"_alias.for_(); - constexpr auto cte = "1"_cte; - - user_table->*&User::id; - u_alias->*&User::id; - cte->*&User::id; -} -#endif - -TEST_CASE("ADL and expression operators") { - struct User { - int id; - }; - struct ScalarFunction { - static const char* name(); - int operator()() const; - }; - - runTests(c(&User::id)); - runTests(column(&User::id)); - runTests(get()); - runTests(alias_column>(&User::id)); - runTests(func()); -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - runTests("a"_col); -#endif -} +#include +#include + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +using namespace sqlite_orm::literals; +#endif +using sqlite_orm::alias_a; +using sqlite_orm::alias_column; +using sqlite_orm::and_; +using sqlite_orm::c; +using sqlite_orm::colalias_a; +using sqlite_orm::column; +using sqlite_orm::func; +using sqlite_orm::get; +using sqlite_orm::or_; +using sqlite_orm::internal::and_condition_t; +using sqlite_orm::internal::binary_operator; +using sqlite_orm::internal::greater_or_equal_t; +using sqlite_orm::internal::greater_than_t; +using sqlite_orm::internal::is_equal_t; +using sqlite_orm::internal::is_not_equal_t; +using sqlite_orm::internal::less_or_equal_t; +using sqlite_orm::internal::less_than_t; +using sqlite_orm::internal::negated_condition_t; +using sqlite_orm::internal::or_condition_t; +using sqlite_orm::polyfill::is_specialization_of_v; + +template +void runTests(E expression) { + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v 42), greater_than_t>); + STATIC_REQUIRE(is_specialization_of_v expression), greater_than_t>); + STATIC_REQUIRE(is_specialization_of_v expression), greater_than_t>); + + STATIC_REQUIRE(is_specialization_of_v= 42), greater_or_equal_t>); + STATIC_REQUIRE(is_specialization_of_v= expression), greater_or_equal_t>); + STATIC_REQUIRE(is_specialization_of_v= expression), greater_or_equal_t>); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + // conc_t + condition_t yield or_condition_t + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); +} + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +TEST_CASE("inline namespace literals expressions") { + constexpr auto u_alias_builder = "u"_alias; + constexpr auto c_alias = "c"_col; + constexpr auto f_scalar_builder = "f"_scalar; + constexpr auto numeric_cte_alias_builder = 1_ctealias; + constexpr auto cte_alias_builder = "1"_cte; +} + +TEST_CASE("ADL and pointer-to-member expressions") { + struct User { + int id; + }; + constexpr auto user_table = c(); + constexpr auto u_alias = "u"_alias.for_(); + constexpr auto cte = "1"_cte; + + user_table->*&User::id; + u_alias->*&User::id; + cte->*&User::id; +} +#endif + +TEST_CASE("ADL and expression operators") { + struct User { + int id; + }; + struct ScalarFunction { + static const char* name(); + int operator()() const; + }; + + runTests(c(&User::id)); + runTests(column(&User::id)); + runTests(get()); + runTests(alias_column>(&User::id)); + runTests(func()); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + runTests("a"_col); +#endif +}