From bbf93f0cbe2b3eb0ed53f340b9d3749305fbd7cc Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 09:22:56 +0200 Subject: [PATCH 1/5] Added feature to use a CTE in a `REPLACE` statement --- dev/storage.h | 7 +++++-- include/sqlite_orm/sqlite_orm.h | 7 +++++-- tests/storage_tests.cpp | 6 +++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index c2370ec8..c18e8f18 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1253,7 +1253,8 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw>, bool> = true> + std::enable_if_t, is_insert_raw, is_replace_raw>, + bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); } @@ -1380,7 +1381,9 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template = true> + template, is_replace_raw>, bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 84f58b6d..1a224276 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23112,7 +23112,8 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw>, bool> = true> + std::enable_if_t, is_insert_raw, is_replace_raw>, + bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); } @@ -23239,7 +23240,9 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template = true> + template, is_replace_raw>, bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index e9e2244a..6ae45ce6 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -583,7 +583,7 @@ TEST_CASE("With clause") { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - SECTION("insert") { + SECTION("crud") { struct Object { int id; }; @@ -596,6 +596,10 @@ TEST_CASE("With clause") { storage.with(cte().as(select(2)), insert(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(2 == storage.last_insert_rowid()); + + storage.with(cte().as(select(2)), + replace(into(), columns(&Object::id), select(data->*1_colalias))); + REQUIRE(storage.changes() == 1); } #endif } From d5980a49817ae911edf11d2efc7c73fc71176c16 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 18:51:24 +0200 Subject: [PATCH 2/5] CTE for raw replace, update all, remove all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also: When collecting table names from column assignments that are part of an “update all” expression, we are only interested in the table name on the left-hand side of the assignment operator expression. --- dev/ast/set.h | 3 ++- dev/prepared_statement.h | 12 +++++++++++ dev/statement_serializer.h | 8 ++++--- dev/storage.h | 15 +++++++++---- include/sqlite_orm/sqlite_orm.h | 38 ++++++++++++++++++++++++++------- tests/storage_tests.cpp | 21 +++++++++++++----- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/dev/ast/set.h b/dev/ast/set.h index e5e66f2e..b997e4c5 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -50,7 +50,8 @@ namespace sqlite_orm { void push_back(assign_t assign) { auto newContext = this->context; newContext.skip_table_name = true; - iterate_ast(assign, this->collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_ast(assign.lhs, this->collector); std::stringstream ss; ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' << serialize(assign.rhs, context); diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 5e79dc20..5b7ebb69 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -142,6 +142,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_update_all_v = polyfill::is_specialization_of::value; + + template + using is_update_all = polyfill::bool_constant>; + template struct remove_all_t { using type = T; @@ -150,6 +156,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_remove_all_v = polyfill::is_specialization_of::value; + + template + using is_remove_all = polyfill::bool_constant>; + template struct get_t { using type = T; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 2131c127..fd3da122 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1362,7 +1362,10 @@ namespace sqlite_orm { template std::set> collect_table_names(const set_t& set, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(set, collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_tuple(set.assigns, [&collector](const auto& assignmentOperator) { + iterate_ast(assignmentOperator.lhs, collector); + }); return std::move(collector.table_names); } @@ -1375,8 +1378,7 @@ namespace sqlite_orm { template = true> std::set> collect_table_names(const T& sel, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); + iterate_ast(sel, collector); return std::move(collector.table_names); } diff --git a/dev/storage.h b/dev/storage.h index c18e8f18..7b0d9d7d 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1253,7 +1253,11 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw, is_replace_raw>, + std::enable_if_t, + is_insert_raw, + is_replace_raw, + is_update_all, + is_remove_all>, bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); @@ -1381,9 +1385,12 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template, is_replace_raw>, bool> = true> + template< + class... CTEs, + class E, + std::enable_if_t< + polyfill::disjunction_v, is_replace_raw, is_update_all, is_remove_all>, + bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1a224276..fb65cd9d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13895,7 +13895,8 @@ namespace sqlite_orm { void push_back(assign_t assign) { auto newContext = this->context; newContext.skip_table_name = true; - iterate_ast(assign, this->collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_ast(assign.lhs, this->collector); std::stringstream ss; ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' << serialize(assign.rhs, context); @@ -14076,6 +14077,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_update_all_v = polyfill::is_specialization_of::value; + + template + using is_update_all = polyfill::bool_constant>; + template struct remove_all_t { using type = T; @@ -14084,6 +14091,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_remove_all_v = polyfill::is_specialization_of::value; + + template + using is_remove_all = polyfill::bool_constant>; + template struct get_t { using type = T; @@ -20505,7 +20518,10 @@ namespace sqlite_orm { template std::set> collect_table_names(const set_t& set, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(set, collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_tuple(set.assigns, [&collector](const auto& assignmentOperator) { + iterate_ast(assignmentOperator.lhs, collector); + }); return std::move(collector.table_names); } @@ -20518,8 +20534,7 @@ namespace sqlite_orm { template = true> std::set> collect_table_names(const T& sel, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); + iterate_ast(sel, collector); return std::move(collector.table_names); } @@ -23112,7 +23127,11 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw, is_replace_raw>, + std::enable_if_t, + is_insert_raw, + is_replace_raw, + is_update_all, + is_remove_all>, bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); @@ -23240,9 +23259,12 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template, is_replace_raw>, bool> = true> + template< + class... CTEs, + class E, + std::enable_if_t< + polyfill::disjunction_v, is_replace_raw, is_update_all, is_remove_all>, + bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 6ae45ce6..37d32398 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -592,14 +592,25 @@ TEST_CASE("With clause") { storage.sync_schema(); - constexpr auto data = "data"_cte; - storage.with(cte().as(select(2)), + constexpr orm_cte_moniker auto data = "data"_cte; + storage.with(cte().as(union_all(select(2), select(3))), insert(into(), columns(&Object::id), select(data->*1_colalias))); - REQUIRE(2 == storage.last_insert_rowid()); + REQUIRE(3 == storage.last_insert_rowid()); - storage.with(cte().as(select(2)), + storage.with(cte().as(union_all(select(2), select(3))), replace(into(), columns(&Object::id), select(data->*1_colalias))); - REQUIRE(storage.changes() == 1); + REQUIRE(storage.changes() == 2); + + storage.with( + cte().as(union_all(select(2), select(3))), + update_all( + set(c(&Object::id) = select(data->*1_colalias, from(), where(data->*1_colalias == &Object::id))), + where(c(&Object::id).in(select(data->*1_colalias))))); + REQUIRE(storage.changes() == 2); + + storage.with(cte().as(union_all(select(2), select(3))), + remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); + REQUIRE(storage.changes() == 2); } #endif } From cff4a64d8cc40c88010803241888f8d478a92aba Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 21:48:13 +0200 Subject: [PATCH 3/5] More `constexpr` factory functions ... ... operators, asterisk, object, where, select, compound, cte. This makes it possible to factor out common expressions now. --- dev/ast/where.h | 4 +- dev/conditions.h | 56 +++++------ dev/core_functions.h | 10 +- dev/cte_moniker.h | 4 +- dev/expression.h | 6 +- dev/operators.h | 24 ++--- dev/select_constraints.h | 44 +++++---- dev/tuple_helper/tuple_iteration.h | 4 +- include/sqlite_orm/sqlite_orm.h | 152 +++++++++++++++-------------- 9 files changed, 154 insertions(+), 150 deletions(-) diff --git a/dev/ast/where.h b/dev/ast/where.h index ebf80b1c..c18d936f 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -26,7 +26,7 @@ namespace sqlite_orm { expression_type expression; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} }; template @@ -46,7 +46,7 @@ namespace sqlite_orm { * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); */ template - internal::where_t where(C expression) { + constexpr internal::where_t where(C expression) { return {std::move(expression)}; } } diff --git a/dev/conditions.h b/dev/conditions.h index 2a9d3be7..d7ac8b93 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -113,7 +113,7 @@ namespace sqlite_orm { struct negated_condition_t : condition_t, negated_condition_string { C c; - negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(C c_) : c(std::move(c_)) {} }; /** @@ -132,9 +132,9 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_condition() = default; + constexpr binary_condition() = default; - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + constexpr binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -849,7 +849,7 @@ namespace sqlite_orm { class T, std::enable_if_t, is_operator_argument>::value, bool> = true> - negated_condition_t operator!(T arg) { + constexpr negated_condition_t operator!(T arg) { return {std::move(arg)}; } @@ -860,7 +860,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_than_t, unwrap_expression_t> operator<(L l, R r) { + constexpr less_than_t, unwrap_expression_t> operator<(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -871,7 +871,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + constexpr less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -882,7 +882,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { + constexpr greater_than_t, unwrap_expression_t> operator>(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -893,7 +893,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + constexpr greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -906,7 +906,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { + constexpr is_equal_t, unwrap_expression_t> operator==(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -919,7 +919,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + constexpr is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -930,7 +930,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + constexpr and_condition_t, unwrap_expression_t> operator&&(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -939,7 +939,7 @@ namespace sqlite_orm { std::enable_if_t< polyfill::disjunction, std::is_base_of>::value, bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { + constexpr or_condition_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -955,7 +955,7 @@ namespace sqlite_orm { polyfill::negation, std::is_base_of>>>::value, bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { + constexpr conc_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -1053,14 +1053,14 @@ namespace sqlite_orm { } template - auto and_(L l, R r) { + constexpr auto and_(L l, R r) { using namespace ::sqlite_orm::internal; return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } template - auto or_(L l, R r) { + constexpr auto or_(L l, R r) { using namespace ::sqlite_orm::internal; return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; @@ -1107,52 +1107,52 @@ namespace sqlite_orm { } template - internal::is_equal_t is_equal(L l, R r) { + constexpr internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_t eq(L l, R r) { + constexpr internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_with_table_t is_equal(R rhs) { + constexpr internal::is_equal_with_table_t is_equal(R rhs) { return {std::move(rhs)}; } template - internal::is_not_equal_t is_not_equal(L l, R r) { + constexpr internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_not_equal_t ne(L l, R r) { + constexpr internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t greater_than(L l, R r) { + constexpr internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t gt(L l, R r) { + constexpr internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t greater_or_equal(L l, R r) { + constexpr internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t ge(L l, R r) { + constexpr internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_than_t less_than(L l, R r) { + constexpr internal::less_than_t less_than(L l, R r) { return {std::move(l), std::move(r)}; } @@ -1166,12 +1166,12 @@ namespace sqlite_orm { } template - internal::less_than_t lt(L l, R r) { + constexpr internal::less_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_or_equal_t less_or_equal(L l, R r) { + constexpr internal::less_or_equal_t less_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } @@ -1185,7 +1185,7 @@ namespace sqlite_orm { } template - internal::less_or_equal_t le(L l, R r) { + constexpr internal::less_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } diff --git a/dev/core_functions.h b/dev/core_functions.h index 4f4c8bd8..bed10999 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -2048,7 +2048,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { + constexpr add_t, unwrap_expression_t> operator+(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2059,7 +2059,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { + constexpr sub_t, unwrap_expression_t> operator-(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2070,7 +2070,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { + constexpr mul_t, unwrap_expression_t> operator*(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2081,7 +2081,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { + constexpr div_t, unwrap_expression_t> operator/(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2092,7 +2092,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { + constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index ebd065aa..f2358462 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -50,7 +50,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #else template>, std::is_convertible>...>, bool> = true> - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #endif }; } diff --git a/dev/expression.h b/dev/expression.h index 1906f9a4..d40b4c63 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -68,12 +68,12 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - T get_from_expression(T value) { + constexpr T get_from_expression(T value) { return std::move(value); } template - T get_from_expression(expression_t expression) { + constexpr T get_from_expression(expression_t expression) { return std::move(expression.value); } @@ -86,7 +86,7 @@ namespace sqlite_orm { * `storage.update(set(c(&User::name) = "Dua Lipa")); */ template - internal::expression_t c(T value) { + constexpr internal::expression_t c(T value) { return {std::move(value)}; } } diff --git a/dev/operators.h b/dev/operators.h index 180747db..85d0e349 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -20,7 +20,7 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + constexpr binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; template @@ -197,7 +197,7 @@ namespace sqlite_orm { * name || '@gmail.com' FROM users */ template - internal::conc_t conc(L l, R r) { + constexpr internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } @@ -205,7 +205,7 @@ namespace sqlite_orm { * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template - internal::add_t add(L l, R r) { + constexpr internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } @@ -213,7 +213,7 @@ namespace sqlite_orm { * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template - internal::sub_t sub(L l, R r) { + constexpr internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } @@ -221,7 +221,7 @@ namespace sqlite_orm { * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users */ template - internal::mul_t mul(L l, R r) { + constexpr internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } @@ -231,7 +231,7 @@ namespace sqlite_orm { * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. */ template - internal::div_t div(L l, R r) { + constexpr internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } @@ -239,32 +239,32 @@ namespace sqlite_orm { * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ template - internal::mod_t mod(L l, R r) { + constexpr internal::mod_t mod(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + constexpr internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + constexpr internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_and_t bitwise_and(L l, R r) { + constexpr internal::bitwise_and_t bitwise_and(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_or_t bitwise_or(L l, R r) { + constexpr internal::bitwise_or_t bitwise_or(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_not_t bitwise_not(T t) { + constexpr internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 0de6af43..e159bef5 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -146,7 +146,7 @@ namespace sqlite_orm { expressions_tuple compound; - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + constexpr compound_operator(expressions_tuple compound) : compound{std::move(compound)} { iterate_tuple(this->compound, [](auto& expression) { expression.highest_level = true; }); @@ -182,7 +182,7 @@ namespace sqlite_orm { struct union_t : public compound_operator, union_base { using typename compound_operator::expressions_tuple; - union_t(expressions_tuple compound, bool all) : + constexpr union_t(expressions_tuple compound, bool all) : compound_operator{std::move(compound)}, union_base{all} {} }; @@ -263,7 +263,7 @@ namespace sqlite_orm { explicit_colrefs_tuple explicitColumns; expression_type subselect; - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + constexpr common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { this->subselect.highest_level = true; } @@ -278,23 +278,25 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> + as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #else template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> as(Compound sel) && { + constexpr common_table_expression, select_t> + as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #endif @@ -416,7 +418,7 @@ namespace sqlite_orm { }; template - void validate_conditions() { + constexpr void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); @@ -483,7 +485,7 @@ namespace sqlite_orm { * Public function for subselect query. Is useful in UNION queries. */ template - internal::select_t select(T t, Args... args) { + constexpr internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), {std::forward(args)...}}; @@ -495,7 +497,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_(E... expressions) { + constexpr internal::union_t union_(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -506,7 +508,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_all(E... expressions) { + constexpr internal::union_t union_all(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -517,13 +519,13 @@ namespace sqlite_orm { * Look through example in examples/except.cpp */ template - internal::except_t except(E... expressions) { + constexpr internal::except_t except(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template - internal::intersect_t intersect(E... expressions) { + constexpr internal::intersect_t intersect(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } @@ -577,7 +579,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool> = true> - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); static_assert((!is_builtin_numeric_column_alias_v && ...), @@ -595,7 +597,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); @@ -614,7 +616,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #else @@ -626,7 +628,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #endif @@ -763,7 +765,7 @@ namespace sqlite_orm { * If you need to fetch results as objects instead of tuples please use `object()`. */ template - internal::asterisk_t asterisk(bool definedOrder = false) { + constexpr internal::asterisk_t asterisk(bool definedOrder = false) { return {definedOrder}; } @@ -775,7 +777,7 @@ namespace sqlite_orm { * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); */ template - auto asterisk(bool definedOrder = false) { + constexpr auto asterisk(bool definedOrder = false) { return asterisk>(definedOrder); } #endif @@ -792,13 +794,13 @@ namespace sqlite_orm { * If you need to fetch results as tuples instead of objects please use `asterisk()`. */ template - internal::object_t object(bool definedOrder = false) { + constexpr internal::object_t object(bool definedOrder = false) { return {definedOrder}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - auto object(bool definedOrder = false) { + constexpr auto object(bool definedOrder = false) { return object>(definedOrder); } #endif diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index e1d7c966..e696cc9a 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -8,7 +8,7 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr(reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= int sink; @@ -34,7 +34,7 @@ namespace sqlite_orm { } #endif template - void iterate_tuple(Tpl&& tpl, L&& lambda) { + constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, std::make_index_sequence>::value>{}, std::forward(lambda)); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fb65cd9d..07207af3 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1639,7 +1639,7 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr(reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= int sink; @@ -1665,7 +1665,7 @@ namespace sqlite_orm { } #endif template - void iterate_tuple(Tpl&& tpl, L&& lambda) { + constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, std::make_index_sequence>::value>{}, std::forward(lambda)); @@ -4147,7 +4147,7 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + constexpr binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; template @@ -4324,7 +4324,7 @@ namespace sqlite_orm { * name || '@gmail.com' FROM users */ template - internal::conc_t conc(L l, R r) { + constexpr internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4332,7 +4332,7 @@ namespace sqlite_orm { * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template - internal::add_t add(L l, R r) { + constexpr internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4340,7 +4340,7 @@ namespace sqlite_orm { * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template - internal::sub_t sub(L l, R r) { + constexpr internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4348,7 +4348,7 @@ namespace sqlite_orm { * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users */ template - internal::mul_t mul(L l, R r) { + constexpr internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4358,7 +4358,7 @@ namespace sqlite_orm { * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. */ template - internal::div_t div(L l, R r) { + constexpr internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4366,32 +4366,32 @@ namespace sqlite_orm { * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ template - internal::mod_t mod(L l, R r) { + constexpr internal::mod_t mod(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + constexpr internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + constexpr internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_and_t bitwise_and(L l, R r) { + constexpr internal::bitwise_and_t bitwise_and(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_or_t bitwise_or(L l, R r) { + constexpr internal::bitwise_or_t bitwise_or(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::bitwise_not_t bitwise_not(T t) { + constexpr internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } @@ -4485,7 +4485,7 @@ namespace sqlite_orm { expression_type expression; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} }; template @@ -4505,7 +4505,7 @@ namespace sqlite_orm { * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); */ template - internal::where_t where(C expression) { + constexpr internal::where_t where(C expression) { return {std::move(expression)}; } } @@ -4717,12 +4717,12 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - T get_from_expression(T value) { + constexpr T get_from_expression(T value) { return std::move(value); } template - T get_from_expression(expression_t expression) { + constexpr T get_from_expression(expression_t expression) { return std::move(expression.value); } @@ -4735,7 +4735,7 @@ namespace sqlite_orm { * `storage.update(set(c(&User::name) = "Dua Lipa")); */ template - internal::expression_t c(T value) { + constexpr internal::expression_t c(T value) { return {std::move(value)}; } } @@ -4852,7 +4852,7 @@ namespace sqlite_orm { struct negated_condition_t : condition_t, negated_condition_string { C c; - negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(C c_) : c(std::move(c_)) {} }; /** @@ -4871,9 +4871,9 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_condition() = default; + constexpr binary_condition() = default; - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + constexpr binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -5588,7 +5588,7 @@ namespace sqlite_orm { class T, std::enable_if_t, is_operator_argument>::value, bool> = true> - negated_condition_t operator!(T arg) { + constexpr negated_condition_t operator!(T arg) { return {std::move(arg)}; } @@ -5599,7 +5599,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_than_t, unwrap_expression_t> operator<(L l, R r) { + constexpr less_than_t, unwrap_expression_t> operator<(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5610,7 +5610,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + constexpr less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5621,7 +5621,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { + constexpr greater_than_t, unwrap_expression_t> operator>(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5632,7 +5632,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + constexpr greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5645,7 +5645,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { + constexpr is_equal_t, unwrap_expression_t> operator==(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5658,7 +5658,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + constexpr is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5669,7 +5669,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + constexpr and_condition_t, unwrap_expression_t> operator&&(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5678,7 +5678,7 @@ namespace sqlite_orm { std::enable_if_t< polyfill::disjunction, std::is_base_of>::value, bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { + constexpr or_condition_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5694,7 +5694,7 @@ namespace sqlite_orm { polyfill::negation, std::is_base_of>>>::value, bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { + constexpr conc_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -5792,14 +5792,14 @@ namespace sqlite_orm { } template - auto and_(L l, R r) { + constexpr auto and_(L l, R r) { using namespace ::sqlite_orm::internal; return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } template - auto or_(L l, R r) { + constexpr auto or_(L l, R r) { using namespace ::sqlite_orm::internal; return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; @@ -5846,52 +5846,52 @@ namespace sqlite_orm { } template - internal::is_equal_t is_equal(L l, R r) { + constexpr internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_t eq(L l, R r) { + constexpr internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_with_table_t is_equal(R rhs) { + constexpr internal::is_equal_with_table_t is_equal(R rhs) { return {std::move(rhs)}; } template - internal::is_not_equal_t is_not_equal(L l, R r) { + constexpr internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_not_equal_t ne(L l, R r) { + constexpr internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t greater_than(L l, R r) { + constexpr internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t gt(L l, R r) { + constexpr internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t greater_or_equal(L l, R r) { + constexpr internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t ge(L l, R r) { + constexpr internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_than_t less_than(L l, R r) { + constexpr internal::less_than_t less_than(L l, R r) { return {std::move(l), std::move(r)}; } @@ -5905,12 +5905,12 @@ namespace sqlite_orm { } template - internal::less_than_t lt(L l, R r) { + constexpr internal::less_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_or_equal_t less_or_equal(L l, R r) { + constexpr internal::less_or_equal_t less_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } @@ -5924,7 +5924,7 @@ namespace sqlite_orm { } template - internal::less_or_equal_t le(L l, R r) { + constexpr internal::less_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } @@ -8086,7 +8086,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { + constexpr add_t, unwrap_expression_t> operator+(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8097,7 +8097,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { + constexpr sub_t, unwrap_expression_t> operator-(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8108,7 +8108,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { + constexpr mul_t, unwrap_expression_t> operator*(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8119,7 +8119,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { + constexpr div_t, unwrap_expression_t> operator/(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8130,7 +8130,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { + constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -8196,7 +8196,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #else template>, std::is_convertible>...>, bool> = true> - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #endif }; } @@ -8600,7 +8600,7 @@ namespace sqlite_orm { expressions_tuple compound; - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + constexpr compound_operator(expressions_tuple compound) : compound{std::move(compound)} { iterate_tuple(this->compound, [](auto& expression) { expression.highest_level = true; }); @@ -8636,7 +8636,7 @@ namespace sqlite_orm { struct union_t : public compound_operator, union_base { using typename compound_operator::expressions_tuple; - union_t(expressions_tuple compound, bool all) : + constexpr union_t(expressions_tuple compound, bool all) : compound_operator{std::move(compound)}, union_base{all} {} }; @@ -8717,7 +8717,7 @@ namespace sqlite_orm { explicit_colrefs_tuple explicitColumns; expression_type subselect; - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + constexpr common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { this->subselect.highest_level = true; } @@ -8732,23 +8732,25 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> + as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #else template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> as(Compound sel) && { + constexpr common_table_expression, select_t> + as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #endif @@ -8870,7 +8872,7 @@ namespace sqlite_orm { }; template - void validate_conditions() { + constexpr void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); @@ -8937,7 +8939,7 @@ namespace sqlite_orm { * Public function for subselect query. Is useful in UNION queries. */ template - internal::select_t select(T t, Args... args) { + constexpr internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), {std::forward(args)...}}; @@ -8949,7 +8951,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_(E... expressions) { + constexpr internal::union_t union_(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -8960,7 +8962,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_all(E... expressions) { + constexpr internal::union_t union_all(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -8971,13 +8973,13 @@ namespace sqlite_orm { * Look through example in examples/except.cpp */ template - internal::except_t except(E... expressions) { + constexpr internal::except_t except(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template - internal::intersect_t intersect(E... expressions) { + constexpr internal::intersect_t intersect(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } @@ -9031,7 +9033,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool> = true> - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); static_assert((!is_builtin_numeric_column_alias_v && ...), @@ -9049,7 +9051,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); @@ -9068,7 +9070,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #else @@ -9080,7 +9082,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #endif @@ -9217,7 +9219,7 @@ namespace sqlite_orm { * If you need to fetch results as objects instead of tuples please use `object()`. */ template - internal::asterisk_t asterisk(bool definedOrder = false) { + constexpr internal::asterisk_t asterisk(bool definedOrder = false) { return {definedOrder}; } @@ -9229,7 +9231,7 @@ namespace sqlite_orm { * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); */ template - auto asterisk(bool definedOrder = false) { + constexpr auto asterisk(bool definedOrder = false) { return asterisk>(definedOrder); } #endif @@ -9246,13 +9248,13 @@ namespace sqlite_orm { * If you need to fetch results as tuples instead of objects please use `asterisk()`. */ template - internal::object_t object(bool definedOrder = false) { + constexpr internal::object_t object(bool definedOrder = false) { return {definedOrder}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - auto object(bool definedOrder = false) { + constexpr auto object(bool definedOrder = false) { return object>(definedOrder); } #endif From 31103782b3ea08e31f3fe1b95b777b34c5f10792 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 21:51:19 +0200 Subject: [PATCH 4/5] Unit tests for serializing the With clause of CRUD expressions --- .../statements/insert_replace.cpp | 34 +++++++++++++++++++ .../statements/remove_all.cpp | 28 +++++++++++---- .../statements/update_all.cpp | 33 +++++++++++++++--- tests/storage_tests.cpp | 13 ++++--- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/tests/statement_serializer_tests/statements/insert_replace.cpp b/tests/statement_serializer_tests/statements/insert_replace.cpp index e30be740..b3882fe2 100644 --- a/tests/statement_serializer_tests/statements/insert_replace.cpp +++ b/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -80,6 +80,23 @@ TEST_CASE("statement_serializer insert/replace") { expected = R"(REPLACE INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(asterisk())); + auto dbObjects2 = + internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, replace(into(), select(asterisk()))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("id", "name") AS (SELECT "users_backup".* FROM "users_backup") REPLACE INTO "users" SELECT "data".* FROM "data")"; + } +#endif +#endif } SECTION("range") { context.replace_bindable_with_question = false; @@ -356,6 +373,23 @@ TEST_CASE("statement_serializer insert/replace") { R"(INSERT OR ROLLBACK INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; } } + SECTION("With clause") { +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(asterisk())); + auto dbObjects2 = + internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, insert(into(), select(asterisk()))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("id", "name") AS (SELECT "users_backup".* FROM "users_backup") INSERT INTO "users" SELECT "data".* FROM "data")"; +#endif +#endif + } } SECTION("range") { context.replace_bindable_with_question = false; diff --git a/tests/statement_serializer_tests/statements/remove_all.cpp b/tests/statement_serializer_tests/statements/remove_all.cpp index 1bfd6c3c..ba30b9c0 100644 --- a/tests/statement_serializer_tests/statements/remove_all.cpp +++ b/tests/statement_serializer_tests/statements/remove_all.cpp @@ -19,19 +19,35 @@ TEST_CASE("statement_serializer remove_all") { std::string expected; SECTION("all") { - auto statement = remove_all(); - value = serialize(statement, context); + auto expression = remove_all(); + value = serialize(expression, context); expected = R"(DELETE FROM "users")"; } SECTION("where") { - auto statement = remove_all(where(&User::id == c(1))); - value = serialize(statement, context); + auto expression = remove_all(where(&User::id == c(1))); + value = serialize(expression, context); 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); + auto expression = remove_all(where(&User::id == c(1)), limit(1)); + value = serialize(expression, context); expected = R"(DELETE FROM "users" WHERE ("id" = 1) LIMIT 1)"; } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(1)); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, remove_all(where(c(&User::id).in(select(data->*1_colalias))))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("1") AS (SELECT 1) DELETE FROM "users" WHERE ("id" IN (SELECT "data"."1" FROM "data")))"; + } +#endif +#endif REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/statements/update_all.cpp b/tests/statement_serializer_tests/statements/update_all.cpp index 4110a1db..006255c8 100644 --- a/tests/statement_serializer_tests/statements/update_all.cpp +++ b/tests/statement_serializer_tests/statements/update_all.cpp @@ -51,10 +51,33 @@ TEST_CASE("statement_serializer update_all") { using context_t = internal::serializer_context; context_t context{dbObjects}; - auto statement = - 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)))"; + std::string value; + std::string expected; + SECTION("select") { + auto expression = update_all(set(c(&Contact::phone) = select(&Customer::phone, where(c(&Customer::id) == 1))), + where(c(&Contact::id) == 1)); + value = serialize(expression, context); + expected = + R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)) WHERE ("contact_id" = 1))"; + } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(&Customer::phone, where(c(&Customer::id) == 1))); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = + with(cteExpression, + update_all(set(c(&Contact::phone) = select(data->*&Customer::phone)), where(c(&Contact::id) == 1))); + + value = serialize(expression, context2); + expected = + R"(WITH "data"("Phone") AS (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)) UPDATE "contacts" SET "phone" = (SELECT "data"."Phone" FROM "data") WHERE ("contact_id" = 1))"; + } +#endif +#endif REQUIRE(value == expected); } diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 37d32398..b241d78e 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -593,23 +593,22 @@ TEST_CASE("With clause") { storage.sync_schema(); constexpr orm_cte_moniker auto data = "data"_cte; - storage.with(cte().as(union_all(select(2), select(3))), - insert(into(), columns(&Object::id), select(data->*1_colalias))); + constexpr auto cteExpression = cte().as(union_all(select(2), select(3))); + + storage.with(cteExpression, insert(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(3 == storage.last_insert_rowid()); - storage.with(cte().as(union_all(select(2), select(3))), - replace(into(), columns(&Object::id), select(data->*1_colalias))); + storage.with(cteExpression, replace(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(storage.changes() == 2); storage.with( - cte().as(union_all(select(2), select(3))), + cteExpression, update_all( set(c(&Object::id) = select(data->*1_colalias, from(), where(data->*1_colalias == &Object::id))), where(c(&Object::id).in(select(data->*1_colalias))))); REQUIRE(storage.changes() == 2); - storage.with(cte().as(union_all(select(2), select(3))), - remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); + storage.with(cteExpression, remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); REQUIRE(storage.changes() == 2); } #endif From fcf86d10ac7a2a8ba39a1876e93c819c6065615e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 23 Jul 2024 12:39:48 +0200 Subject: [PATCH 5/5] Provided a table reference for the "dbstat" eponymous virtual table --- dev/eponymous_vtabs/dbstat.h | 5 +++++ include/sqlite_orm/sqlite_orm.h | 6 ++++++ tests/builtin_tables.cpp | 4 ++++ tests/prepared_statement_tests/get_all.cpp | 4 ++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dev/eponymous_vtabs/dbstat.h b/dev/eponymous_vtabs/dbstat.h index 1b674ed9..3c56b44b 100644 --- a/dev/eponymous_vtabs/dbstat.h +++ b/dev/eponymous_vtabs/dbstat.h @@ -6,6 +6,7 @@ #include "../schema/column.h" #include "../schema/table.h" +#include "../column_pointer.h" namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB @@ -35,5 +36,9 @@ namespace sqlite_orm { make_column("pgoffset", &dbstat::pgoffset), make_column("pgsize", &dbstat::pgsize)); } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 84f58b6d..bc860c2d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23768,6 +23768,8 @@ namespace sqlite_orm { // #include "../schema/table.h" +// #include "../column_pointer.h" + namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -23796,6 +23798,10 @@ namespace sqlite_orm { make_column("pgoffset", &dbstat::pgoffset), make_column("pgsize", &dbstat::pgsize)); } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/tests/builtin_tables.cpp b/tests/builtin_tables.cpp index 41fe960a..e82d8b7a 100644 --- a/tests/builtin_tables.cpp +++ b/tests/builtin_tables.cpp @@ -50,6 +50,10 @@ TEST_CASE("builtin tables") { auto dbstatRows = storage.get_all(); std::ignore = dbstatRows; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + dbstatRows = storage.get_all(); +#endif } #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/tests/prepared_statement_tests/get_all.cpp b/tests/prepared_statement_tests/get_all.cpp index ff7dd3d1..26906a12 100644 --- a/tests/prepared_statement_tests/get_all.cpp +++ b/tests/prepared_statement_tests/get_all.cpp @@ -217,8 +217,8 @@ TEST_CASE("Prepared get all") { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("from table reference") { - constexpr auto schema = c(); - auto statement = storage.prepare(get_all(where(schema->*&sqlite_master::type == "table"))); + auto statement = + storage.prepare(get_all(where(sqlite_master_table->*&sqlite_master::type == "table"))); auto str = storage.dump(statement); testSerializing(statement); }