diff --git a/dev/conditions.h b/dev/conditions.h index d7ac8b937..7ee3dcff8 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -111,9 +111,11 @@ namespace sqlite_orm { */ template struct negated_condition_t : condition_t, negated_condition_string { - C c; + using argument_type = C; - constexpr negated_condition_t(C c_) : c(std::move(c_)) {} + argument_type c; + + constexpr negated_condition_t(argument_type arg) : c(std::move(arg)) {} }; /** @@ -153,7 +155,7 @@ namespace sqlite_orm { * Result of and operator */ template - struct and_condition_t : binary_condition { + struct and_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -169,7 +171,7 @@ namespace sqlite_orm { * Result of or operator */ template - struct or_condition_t : binary_condition { + struct or_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; diff --git a/dev/core_functions.h b/dev/core_functions.h index bed109992..3c4136b52 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -2095,6 +2095,58 @@ namespace sqlite_orm { constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } + + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr bitwise_not_t> operator~(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_left_t, unwrap_expression_t> operator<<(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_right_t, unwrap_expression_t> operator>>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_and_t, unwrap_expression_t> operator&(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_or_t, unwrap_expression_t> operator|(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } } template diff --git a/dev/expression.h b/dev/expression.h index d40b4c635..39a86aec3 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -68,15 +68,25 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - constexpr T get_from_expression(T value) { + constexpr T get_from_expression(T&& value) { return std::move(value); } template - constexpr T get_from_expression(expression_t expression) { + constexpr const T& get_from_expression(const T& value) { + return value; + } + + template + constexpr T get_from_expression(expression_t&& expression) { return std::move(expression.value); } + template + constexpr const T& get_from_expression(const expression_t& expression) { + return expression.value; + } + template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index fd3da122c..60cf5586d 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -35,6 +35,7 @@ #include "type_printer.h" #include "field_printer.h" #include "literal.h" +#include "expression.h" #include "table_name_collector.h" #include "column_names_getter.h" #include "cte_column_names_collector.h" @@ -721,11 +722,23 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type& statement, const Ctx& context) const { + std::string operator()(const statement_type& expression, 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 parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << statement.serialize() << " "; - auto cString = serialize(statement.argument, context); - ss << " (" << cString << " )"; + ss << expression.serialize(); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.argument), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; @@ -735,11 +748,23 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type& c, const Ctx& context) const { + std::string operator()(const statement_type& expression, 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 parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = serialize(c.c, context); - ss << " (" << cString << " )"; + ss << static_cast(expression) << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.c), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9adac345a..ff4507de1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4717,15 +4717,25 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - constexpr T get_from_expression(T value) { + constexpr T get_from_expression(T&& value) { return std::move(value); } template - constexpr T get_from_expression(expression_t expression) { + constexpr const T& get_from_expression(const T& value) { + return value; + } + + template + constexpr T get_from_expression(expression_t&& expression) { return std::move(expression.value); } + template + constexpr const T& get_from_expression(const expression_t& expression) { + return expression.value; + } + template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } @@ -4850,9 +4860,11 @@ namespace sqlite_orm { */ template struct negated_condition_t : condition_t, negated_condition_string { - C c; + using argument_type = C; + + argument_type c; - constexpr negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(argument_type arg) : c(std::move(arg)) {} }; /** @@ -4892,7 +4904,7 @@ namespace sqlite_orm { * Result of and operator */ template - struct and_condition_t : binary_condition { + struct and_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -4908,7 +4920,7 @@ namespace sqlite_orm { * Result of or operator */ template - struct or_condition_t : binary_condition { + struct or_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -8133,6 +8145,58 @@ namespace sqlite_orm { constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } + + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr bitwise_not_t> operator~(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_left_t, unwrap_expression_t> operator<<(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_right_t, unwrap_expression_t> operator>>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_and_t, unwrap_expression_t> operator&(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_or_t, unwrap_expression_t> operator|(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } } template @@ -18463,6 +18527,8 @@ namespace sqlite_orm { // #include "literal.h" +// #include "expression.h" + // #include "table_name_collector.h" // #include "column_names_getter.h" @@ -19879,11 +19945,23 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type& statement, const Ctx& context) const { + std::string operator()(const statement_type& expression, 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 parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << statement.serialize() << " "; - auto cString = serialize(statement.argument, context); - ss << " (" << cString << " )"; + ss << expression.serialize(); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.argument), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; @@ -19893,11 +19971,23 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type& c, const Ctx& context) const { + std::string operator()(const statement_type& expression, 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 parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = serialize(c.c, context); - ss << " (" << cString << " )"; + ss << static_cast(expression) << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.c), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db24c65f0..d3ebf4503 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,15 +57,15 @@ add_executable(unit_tests built_in_functions_tests/math_functions.cpp user_defined_functions.cpp constraints/composite_key.cpp - operators/arithmetic_operators.cpp operators/like.cpp operators/glob.cpp operators/in.cpp operators/cast.cpp operators/is_null.cpp - operators/not_operator.cpp - operators/bitwise.cpp + operators/arithmetic_operators.cpp + operators/bitwise_operators.cpp operators/binary_operators.cpp + operators/not_operator.cpp prepared_statement_tests/select.cpp prepared_statement_tests/get_all.cpp prepared_statement_tests/get_all_pointer.cpp diff --git a/tests/operators/bitwise.cpp b/tests/operators/bitwise_operators.cpp similarity index 50% rename from tests/operators/bitwise.cpp rename to tests/operators/bitwise_operators.cpp index 7492a2c12..c65241555 100644 --- a/tests/operators/bitwise.cpp +++ b/tests/operators/bitwise_operators.cpp @@ -18,46 +18,50 @@ TEST_CASE("bitwise operators") { storage.sync_schema(); { - auto rows = storage.select(bitwise_or(60, 13)); + auto rows = storage.select(union_(select(bitwise_or(60, 13)), select(c(60) | 13))); REQUIRE(rows == std::vector{61}); } { - auto rows = storage.select(bitwise_and(60, 13)); + auto rows = storage.select(union_(select(bitwise_and(60, 13)), select(c(60) & 13))); REQUIRE(rows == std::vector{12}); } { - auto rows = storage.select(bitwise_shift_left(60, 2)); + auto rows = storage.select(union_(select(bitwise_shift_left(60, 2)), select(c(60) << 2))); REQUIRE(rows == std::vector{240}); } { - auto rows = storage.select(bitwise_shift_right(60, 2)); + auto rows = storage.select(union_(select(bitwise_shift_right(60, 2)), select(c(60) >> 2))); REQUIRE(rows == std::vector{15}); } { - auto rows = storage.select(bitwise_not(60)); + auto rows = storage.select(union_(select(bitwise_not(60)), select(~c(60)))); REQUIRE(rows == std::vector{-61}); } storage.insert(Entry{60, 13}); { - auto rows = storage.select(bitwise_or(&Entry::lhs, &Entry::rhs)); + auto rows = + storage.select(union_(select(bitwise_or(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) | &Entry::rhs))); REQUIRE(rows == std::vector{61}); } { - auto rows = storage.select(bitwise_and(&Entry::lhs, &Entry::rhs)); + auto rows = + storage.select(union_(select(bitwise_and(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) & &Entry::rhs))); REQUIRE(rows == std::vector{12}); } storage.remove_all(); storage.insert(Entry{60, 2}); { - auto rows = storage.select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)); + auto rows = storage.select( + union_(select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) << &Entry::rhs))); REQUIRE(rows == std::vector{240}); } { - auto rows = storage.select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)); + auto rows = storage.select( + union_(select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) >> &Entry::rhs))); REQUIRE(rows == std::vector{15}); } { - auto rows = storage.select(bitwise_not(&Entry::lhs)); + auto rows = storage.select(union_(select(bitwise_not(&Entry::lhs)), select(~c(&Entry::lhs)))); REQUIRE(rows == std::vector{-61}); } } diff --git a/tests/statement_serializer_tests/bitwise_operators.cpp b/tests/statement_serializer_tests/bitwise_operators.cpp new file mode 100644 index 000000000..b5b119c95 --- /dev/null +++ b/tests/statement_serializer_tests/bitwise_operators.cpp @@ -0,0 +1,71 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer bitwise operators") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + decltype(value) expected; + SECTION("bitwise_or") { + SECTION("func") { + value = serialize(bitwise_or(3, 5), context); + } + SECTION("operator") { + value = serialize(c(3) | 5, context); + } + expected = "3 | 5"; + } + SECTION("bitwise_and") { + SECTION("func") { + value = serialize(bitwise_and(5, -9), context); + } + SECTION("operator") { + value = serialize(c(5) & -9, context); + } + expected = "5 & -9"; + } + SECTION("bitwise_shift_left") { + SECTION("func") { + value = serialize(bitwise_shift_left(10, 1), context); + } + SECTION("operator") { + value = serialize(c(10) << 1, context); + } + expected = "10 << 1"; + } + SECTION("bitwise_shift_right") { + SECTION("func") { + value = serialize(bitwise_shift_right(10, 2), context); + } + SECTION("operator") { + value = serialize(c(10) >> 2, context); + } + expected = "10 >> 2"; + } + SECTION("bitwise_not") { + SECTION("func") { + value = serialize(bitwise_not(20), context); + } + SECTION("operator") { + value = serialize(~c(20), context); + } + expected = "~20"; + } + 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/logical_operators.cpp b/tests/statement_serializer_tests/logical_operators.cpp index 312f4c292..00fdae9c5 100644 --- a/tests/statement_serializer_tests/logical_operators.cpp +++ b/tests/statement_serializer_tests/logical_operators.cpp @@ -63,6 +63,20 @@ TEST_CASE("statement_serializer logical operators") { expected = R"(("id" = 5) OR ("name" = 'Ariana'))"; } } + SECTION("not") { + SECTION("simple") { + SECTION("operator") { + stringValue = serialize(!c(20), context); + } + expected = "NOT 20"; + } + SECTION("complex") { + SECTION("operator") { + stringValue = serialize(!(c(20) and 1), context); + } + expected = "NOT (20 AND 1)"; + } + } SECTION("in") { SECTION("static in") { auto inValue = c(&User::id).in(1, 2, 3); diff --git a/tests/statement_serializer_tests/schema/trigger.cpp b/tests/statement_serializer_tests/schema/trigger.cpp index 28c9d00ff..99d669801 100644 --- a/tests/statement_serializer_tests/schema/trigger.cpp +++ b/tests/statement_serializer_tests/schema/trigger.cpp @@ -37,7 +37,7 @@ TEST_CASE("statement_serializer trigger") { value = serialize(expression, context); expected = R"(CREATE TRIGGER IF NOT EXISTS "validate_email_before_insert_leads" BEFORE INSERT ON "leads" BEGIN SELECT )" - R"(CASE WHEN NOT (NEW."email" LIKE '%_@__%.__%' ) THEN RAISE(ABORT, 'Invalid email address') END; END)"; + R"(CASE WHEN NOT NEW."email" LIKE '%_@__%.__%' THEN RAISE(ABORT, 'Invalid email address') END; END)"; } REQUIRE(value == expected); } diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index 0a914afa0..25b19491d 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -105,11 +105,16 @@ TEST_CASE("column_result_of_t") { runTest(c(&User::id) / 5); runTest(mod(&User::id, 5)); runTest(c(&User::id) % 5); + runTest(bitwise_not(&User::id)); + runTest(~c(&User::id)); runTest(bitwise_shift_left(&User::id, 4)); + runTest(c(&User::id) << 4); runTest(bitwise_shift_right(&User::id, 4)); + runTest(c(&User::id) >> 4); runTest(bitwise_and(&User::id, 4)); + runTest(c(&User::id) & 4); runTest(bitwise_or(&User::id, 4)); - runTest(bitwise_not(&User::id)); + runTest(c(&User::id) | 4); runTest(rowid()); runTest(oid()); runTest(_rowid_()); diff --git a/tests/static_tests/operators_adl.cpp b/tests/static_tests/operators_adl.cpp index 74c5df20d..cdfdb63c4 100644 --- a/tests/static_tests/operators_adl.cpp +++ b/tests/static_tests/operators_adl.cpp @@ -15,6 +15,11 @@ 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::bitwise_and_t; +using sqlite_orm::internal::bitwise_not_t; +using sqlite_orm::internal::bitwise_or_t; +using sqlite_orm::internal::bitwise_shift_left_t; +using sqlite_orm::internal::bitwise_shift_right_t; using sqlite_orm::internal::greater_or_equal_t; using sqlite_orm::internal::greater_than_t; using sqlite_orm::internal::is_equal_t; @@ -106,6 +111,12 @@ void runTests(E expression) { // conc_t + condition_t yield or_condition_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> expression), binary_operator>); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); } TEST_CASE("inline namespace literals expressions") {