diff --git a/dev/alias.h b/dev/alias.h index b2073dbab..819c26925 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -7,7 +7,7 @@ namespace sqlite_orm { /** - * This is base class for every class which is used as a custom table alias. + * This is base class for every class which is used as a custom table alias, column alias or expression alias. * For more information please look through self_join.cpp example */ struct alias_tag {}; @@ -16,7 +16,7 @@ namespace sqlite_orm { /** * This is a common built-in class used for custom single character table aliases. - * Also you can use language aliases `alias_a`, `alias_b` etc. instead + * For convenience there exist type aliases `alias_a`, `alias_b`, ... */ template struct table_alias : alias_tag { @@ -37,7 +37,7 @@ namespace sqlite_orm { column_type column; - alias_column_t(){}; + alias_column_t() {} alias_column_t(column_type column_) : column(std::move(column_)) {} }; @@ -72,6 +72,17 @@ namespace sqlite_orm { expression_type expression; }; + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + */ + template + struct column_alias : alias_tag { + static std::string get() { + return std::string(1u, A); + } + }; + template struct alias_holder { using type = T; @@ -151,4 +162,14 @@ namespace sqlite_orm { using alias_y = internal::table_alias; template using alias_z = internal::table_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; } diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 9f7d1e0a3..d260655f4 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -654,9 +654,23 @@ namespace sqlite_orm { } }; + /** + * Column alias or literal + */ template - struct ast_iterator, void> { - using node_type = order_by_t; + struct ast_iterator< + T, + std::enable_if_t, + polyfill::is_specialization_of>>> { + using node_type = T; + + template + void operator()(const node_type& /*node*/, const L& /*l*/) const {} + }; + + template + struct ast_iterator, void> { + using node_type = order_by_t; template void operator()(const node_type& node, const L& l) const { diff --git a/dev/conditions.h b/dev/conditions.h index a1e7755c1..92f6bf16c 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -6,12 +6,16 @@ #include // std::tuple, std::tuple_size #include // std::stringstream +#include "cxx_polyfill.h" +#include "type_traits.h" #include "collate_argument.h" #include "constraints.h" #include "optional_container.h" #include "serializer_context.h" #include "tags.h" #include "expression.h" +#include "type_printer.h" +#include "literal.h" namespace sqlite_orm { @@ -1172,14 +1176,28 @@ namespace sqlite_orm { } /** - * ORDER BY column - * Example: storage.select(&User::name, order_by(&User::id)) + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) */ - template + template> = true> internal::order_by_t order_by(O o) { return {std::move(o)}; } + /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template> = true> + internal::order_by_t> order_by(O o) { + return {{std::move(o)}}; + } + /** * ORDER BY column1, column2 * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 16e9d6331..ce6547637 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -123,9 +123,9 @@ namespace sqlite_orm { const auto& get(const internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; + using node_tuple = internal::node_tuple_t; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + using result_tupe = std::tuple_element_t(N), bind_tuple>; const result_tupe* result = nullptr; auto index = -1; internal::iterate_ast(statement.expression, [&result, &index](auto& node) { @@ -146,9 +146,9 @@ namespace sqlite_orm { auto& get(internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; + using node_tuple = internal::node_tuple_t; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + using result_tupe = std::tuple_element_t(N), bind_tuple>; result_tupe* result = nullptr; auto index = -1; internal::iterate_ast(statement.expression, [&result, &index](auto& node) { diff --git a/dev/literal.h b/dev/literal.h new file mode 100644 index 000000000..52683eb9b --- /dev/null +++ b/dev/literal.h @@ -0,0 +1,19 @@ +#pragma once + +#include // std::move + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; + + T value; + }; + + } +} diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 3bd7f2c30..3cfff1c1f 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -25,6 +25,12 @@ namespace sqlite_orm { namespace internal { template + struct node_tuple; + + template + using node_tuple_t = typename node_tuple::type; + + template struct node_tuple { using type = std::tuple; }; @@ -35,55 +41,58 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = typename node_tuple>::type; - }; + struct node_tuple, void> : node_tuple> {}; template struct node_tuple, void> { - using args_tuple = typename node_tuple>::type; - using expression_tuple = typename node_tuple::type; + using args_tuple = node_tuple_t>; + using expression_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, std::tuple>, void> { - using type = typename node_tuple>::type; - }; + struct node_tuple, std::tuple>, void> + : node_tuple> {}; template struct node_tuple, void> { - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = where_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Literal + */ + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; template struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; using type = typename conc_tuple::type; }; @@ -92,30 +101,27 @@ namespace sqlite_orm { using node_type = binary_operator; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = columns_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = dynamic_in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename conc_tuple::type...>::type; + using left_tuple = node_tuple_t; + using right_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; @@ -124,266 +130,188 @@ namespace sqlite_orm { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = select_t; - using columns_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; + using columns_tuple = node_tuple_t; + using args_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = insert_raw_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = replace_raw_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = into_t; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = values_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = std::tuple; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = get_all_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = get_all_pointer_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { - using node_type = get_all_optional_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, Wargs...>, void> { - using node_type = update_all_t, Wargs...>; - using set_tuple = typename conc_tuple::type...>::type; - using conditions_tuple = typename conc_tuple::type...>::type; + using set_tuple = typename conc_tuple...>::type; + using conditions_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = remove_all_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = having_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = cast_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = exists_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = optional_container; - using type = typename node_tuple::type; - }; - - template<> - struct node_tuple, void> { - using node_type = optional_container; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = like_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; - using escape_tuple = typename node_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using escape_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = glob_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = between_t; - using expression_tuple = typename node_tuple::type; - using lower_tuple = typename node_tuple::type; - using upper_tuple = typename node_tuple::type; + using expression_tuple = node_tuple_t; + using lower_tuple = node_tuple_t; + using upper_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = named_collate; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_not_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = negated_condition_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = built_in_function_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = built_in_aggregate_function_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = filtered_aggregate_function; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = function_call; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = left_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = on_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; // note: not strictly necessary as there's no binding support for USING; // we provide it nevertheless, in line with on_t. template - struct node_tuple, void> { - using node_type = using_t; - using type = typename node_tuple>::type; - }; + struct node_tuple, void> : node_tuple> {}; template - struct node_tuple, void> { - using node_type = join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = left_outer_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = inner_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = simple_case_t; - using case_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; - using else_tuple = typename node_tuple::type; + using case_tuple = node_tuple_t; + using args_tuple = typename conc_tuple...>::type; + using else_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = std::pair; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = as_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = limit_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = typename conc_tuple, node_tuple_t>::type; }; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = typename conc_tuple, node_tuple_t>::type; }; } } diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index 9314faefd..05f9748e0 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -27,7 +27,7 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(orderBy.expression, newContext); - ss << columnName << " "; + ss << columnName; if(!orderBy._collate_argument.empty()) { ss << " COLLATE " << orderBy._collate_argument; } @@ -54,16 +54,16 @@ namespace sqlite_orm { std::string entryString; { std::stringstream ss; - ss << entry.name << " "; + ss << entry.name; if(!entry._collate_argument.empty()) { - ss << "COLLATE " << entry._collate_argument << " "; + ss << " COLLATE " << entry._collate_argument; } switch(entry.asc_desc) { case 1: - ss << "ASC"; + ss << " ASC"; break; case -1: - ss << "DESC"; + ss << " DESC"; break; } entryString = ss.str(); diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index aa1c55349..e2c6f71e9 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -29,6 +29,7 @@ #include "pointer_value.h" #include "type_printer.h" #include "field_printer.h" +#include "literal.h" #include "table_name_collector.h" #include "column_names_getter.h" #include "order_by_serializer.h" @@ -126,6 +127,24 @@ namespace sqlite_orm { } }; + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + template struct statement_serializer, void> { using statement_type = filtered_aggregate_function; @@ -2095,8 +2114,7 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - auto orderByString = serialize_order_by(orderBy, context); - ss << orderByString << " "; + ss << serialize_order_by(orderBy, context); return ss.str(); } }; diff --git a/dev/type_printer.h b/dev/type_printer.h index 2ae90f87b..2c3c93d2c 100644 --- a/dev/type_printer.h +++ b/dev/type_printer.h @@ -13,7 +13,7 @@ namespace sqlite_orm { * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) */ template - struct type_printer; + struct type_printer {}; struct integer_printer { inline const std::string& print() { diff --git a/examples/column_aliases.cpp b/examples/column_aliases.cpp new file mode 100644 index 000000000..671809829 --- /dev/null +++ b/examples/column_aliases.cpp @@ -0,0 +1,76 @@ +#include +#include +#include + +using std::cout; +using std::endl; +using std::system_error; +using namespace sqlite_orm; + +void marvel_hero_ordered_by_o_pos() { + struct MarvelHero { + int id = 0; + std::string name; + std::string abilities; + short points = 0; + }; + + auto storage = make_storage("", + make_table("marvel", + make_column("id", &MarvelHero::id, primary_key()), + make_column("name", &MarvelHero::name), + make_column("abilities", &MarvelHero::abilities), + make_column("points", &MarvelHero::points))); + storage.sync_schema(); + + // insert values + storage.transaction([&storage] { + storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist", 5}); + storage.insert(MarvelHero{-1, "Thor", "Storm god", -10}); + storage.insert(MarvelHero{-1, "Vision", "Min Stone", 4}); + storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield", -3}); + storage.insert(MarvelHero{-1, "Hulk", "Strength", -15}); + storage.insert(MarvelHero{-1, "Star Lord", "Humor", 19}); + storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman", 16}); + storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye", -11}); + storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow", 8}); + storage.insert(MarvelHero{-1, "Groot", "I am Groot!", 2}); + + return true; + }); + + { + // SELECT name, instr(abilities, 'o') i + // FROM marvel + // WHERE i > 0 + // ORDER BY i + auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), + where(greater_than(get(), 0)), + order_by(get())); + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << '\n'; + } + } + cout << endl; + { + // SELECT name, instr(abilities, 'o') + // FROM marvel + // ORDER BY 2 + auto rows = + storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), order_by(2)); + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << '\n'; + } + } + cout << endl; +} + +int main() { + try { + marvel_hero_ordered_by_o_pos(); + } catch(const system_error& e) { + cout << "[" << e.code() << "] " << e.what(); + } + + return 0; +} diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index 9239a8c82..864d92dbf 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -1109,8 +1109,10 @@ int main(int, char** argv) { // SELECT name, instr(abilities, 'o') o_pos // FROM marvel // WHERE o_pos > 0 + // ORDER BY o_pos auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), - where(greater_than(get(), 0))); + where(greater_than(get(), 0)), + order_by(get())); for(auto& row: rows) { cout << get<0>(row) << '\t' << get<1>(row) << endl; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 51825903c..8595b6fdb 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -702,7 +702,7 @@ namespace sqlite_orm { * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) */ template - struct type_printer; + struct type_printer {}; struct integer_printer { inline const std::string& print() { @@ -2592,6 +2592,10 @@ namespace sqlite_orm { #include // std::tuple, std::tuple_size #include // std::stringstream +// #include "cxx_polyfill.h" + +// #include "type_traits.h" + // #include "collate_argument.h" // #include "constraints.h" @@ -2748,6 +2752,28 @@ namespace sqlite_orm { } } +// #include "type_printer.h" + +// #include "literal.h" + +#include // std::move + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; + + T value; + }; + + } +} + namespace sqlite_orm { namespace internal { @@ -3907,14 +3933,28 @@ namespace sqlite_orm { } /** - * ORDER BY column - * Example: storage.select(&User::name, order_by(&User::id)) + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) */ - template + template> = true> internal::order_by_t order_by(O o) { return {std::move(o)}; } + /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template> = true> + internal::order_by_t> order_by(O o) { + return {{std::move(o)}}; + } + /** * ORDER BY column1, column2 * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) @@ -3997,7 +4037,7 @@ namespace sqlite_orm { namespace sqlite_orm { /** - * This is base class for every class which is used as a custom table alias. + * This is base class for every class which is used as a custom table alias, column alias or expression alias. * For more information please look through self_join.cpp example */ struct alias_tag {}; @@ -4006,7 +4046,7 @@ namespace sqlite_orm { /** * This is a common built-in class used for custom single character table aliases. - * Also you can use language aliases `alias_a`, `alias_b` etc. instead + * For convenience there exist type aliases `alias_a`, `alias_b`, ... */ template struct table_alias : alias_tag { @@ -4027,7 +4067,7 @@ namespace sqlite_orm { column_type column; - alias_column_t(){}; + alias_column_t() {} alias_column_t(column_type column_) : column(std::move(column_)) {} }; @@ -4062,6 +4102,17 @@ namespace sqlite_orm { expression_type expression; }; + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + */ + template + struct column_alias : alias_tag { + static std::string get() { + return std::string(1u, A); + } + }; + template struct alias_holder { using type = T; @@ -4141,6 +4192,16 @@ namespace sqlite_orm { using alias_y = internal::table_alias; template using alias_z = internal::table_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; } #pragma once @@ -12460,9 +12521,23 @@ namespace sqlite_orm { } }; + /** + * Column alias or literal + */ template - struct ast_iterator, void> { - using node_type = order_by_t; + struct ast_iterator< + T, + std::enable_if_t, + polyfill::is_specialization_of>>> { + using node_type = T; + + template + void operator()(const node_type& /*node*/, const L& /*l*/) const {} + }; + + template + struct ast_iterator, void> { + using node_type = order_by_t; template void operator()(const node_type& node, const L& l) const { @@ -14167,6 +14242,8 @@ namespace sqlite_orm { // #include "field_printer.h" +// #include "literal.h" + // #include "table_name_collector.h" #include // std::set @@ -14424,7 +14501,7 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(orderBy.expression, newContext); - ss << columnName << " "; + ss << columnName; if(!orderBy._collate_argument.empty()) { ss << " COLLATE " << orderBy._collate_argument; } @@ -14451,16 +14528,16 @@ namespace sqlite_orm { std::string entryString; { std::stringstream ss; - ss << entry.name << " "; + ss << entry.name; if(!entry._collate_argument.empty()) { - ss << "COLLATE " << entry._collate_argument << " "; + ss << " COLLATE " << entry._collate_argument; } switch(entry.asc_desc) { case 1: - ss << "ASC"; + ss << " ASC"; break; case -1: - ss << "DESC"; + ss << " DESC"; break; } entryString = ss.str(); @@ -14581,6 +14658,24 @@ namespace sqlite_orm { } }; + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + template struct statement_serializer, void> { using statement_type = filtered_aggregate_function; @@ -16550,8 +16645,7 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - auto orderByString = serialize_order_by(orderBy, context); - ss << orderByString << " "; + ss << serialize_order_by(orderBy, context); return ss.str(); } }; @@ -18588,6 +18682,12 @@ namespace sqlite_orm { namespace internal { template + struct node_tuple; + + template + using node_tuple_t = typename node_tuple::type; + + template struct node_tuple { using type = std::tuple; }; @@ -18598,55 +18698,58 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = typename node_tuple>::type; - }; + struct node_tuple, void> : node_tuple> {}; template struct node_tuple, void> { - using args_tuple = typename node_tuple>::type; - using expression_tuple = typename node_tuple::type; + using args_tuple = node_tuple_t>; + using expression_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, std::tuple>, void> { - using type = typename node_tuple>::type; - }; + struct node_tuple, std::tuple>, void> + : node_tuple> {}; template struct node_tuple, void> { - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = where_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Literal + */ + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; template struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; using type = typename conc_tuple::type; }; @@ -18655,30 +18758,27 @@ namespace sqlite_orm { using node_type = binary_operator; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = columns_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = dynamic_in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename conc_tuple::type...>::type; + using left_tuple = node_tuple_t; + using right_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; @@ -18687,266 +18787,188 @@ namespace sqlite_orm { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = select_t; - using columns_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; + using columns_tuple = node_tuple_t; + using args_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = insert_raw_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = replace_raw_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = into_t; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = values_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = std::tuple; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = get_all_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = get_all_pointer_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { - using node_type = get_all_optional_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, Wargs...>, void> { - using node_type = update_all_t, Wargs...>; - using set_tuple = typename conc_tuple::type...>::type; - using conditions_tuple = typename conc_tuple::type...>::type; + using set_tuple = typename conc_tuple...>::type; + using conditions_tuple = typename conc_tuple...>::type; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = remove_all_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = having_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = cast_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = exists_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = optional_container; - using type = typename node_tuple::type; - }; - - template<> - struct node_tuple, void> { - using node_type = optional_container; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = like_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; - using escape_tuple = typename node_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using escape_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = glob_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = between_t; - using expression_tuple = typename node_tuple::type; - using lower_tuple = typename node_tuple::type; - using upper_tuple = typename node_tuple::type; + using expression_tuple = node_tuple_t; + using lower_tuple = node_tuple_t; + using upper_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = named_collate; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_not_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = negated_condition_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = built_in_function_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = built_in_aggregate_function_t; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template struct node_tuple, void> { - using node_type = filtered_aggregate_function; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = function_call; - using type = typename conc_tuple::type...>::type; + using type = typename conc_tuple...>::type; }; template - struct node_tuple, void> { - using node_type = left_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = on_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; // note: not strictly necessary as there's no binding support for USING; // we provide it nevertheless, in line with on_t. template - struct node_tuple, void> { - using node_type = using_t; - using type = typename node_tuple>::type; - }; + struct node_tuple, void> : node_tuple> {}; template - struct node_tuple, void> { - using node_type = join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = left_outer_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = inner_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = simple_case_t; - using case_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; - using else_tuple = typename node_tuple::type; + using case_tuple = node_tuple_t; + using args_tuple = typename conc_tuple...>::type; + using else_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template struct node_tuple, void> { - using node_type = std::pair; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = as_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = limit_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = typename conc_tuple, node_tuple_t>::type; }; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = typename conc_tuple, node_tuple_t>::type; }; } } @@ -19078,9 +19100,9 @@ namespace sqlite_orm { const auto& get(const internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; + using node_tuple = internal::node_tuple_t; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + using result_tupe = std::tuple_element_t(N), bind_tuple>; const result_tupe* result = nullptr; auto index = -1; internal::iterate_ast(statement.expression, [&result, &index](auto& node) { @@ -19101,9 +19123,9 @@ namespace sqlite_orm { auto& get(internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; + using node_tuple = internal::node_tuple_t; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + using result_tupe = std::tuple_element_t(N), bind_tuple>; result_tupe* result = nullptr; auto index = -1; internal::iterate_ast(statement.expression, [&result, &index](auto& node) { diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index 96eed6f13..746cfd852 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -13,6 +13,11 @@ TEST_CASE("ast_iterator") { auto lambda = [&typeIndexes](auto &value) { typeIndexes.push_back(typeid(value)); }; + SECTION("bindables") { + auto node = select(1); + expected.push_back(typeid(int)); + internal::iterate_ast(node, lambda); + } SECTION("aggregate functions") { SECTION("avg") { auto node = avg(&User::id); @@ -156,10 +161,30 @@ TEST_CASE("ast_iterator") { internal::iterate_ast(node, lambda); } SECTION("order_by") { - auto node = order_by(c(&User::id) == 0); - expected.push_back(typeid(&User::id)); - expected.push_back(typeid(int)); - internal::iterate_ast(node, lambda); + SECTION("expression") { + auto node = order_by(c(&User::id) == 0); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + internal::iterate_ast(node, lambda); + } + SECTION("bindable") { + auto node = order_by(""); + expected.push_back(typeid(const char *)); + internal::iterate_ast(node, lambda); + } + SECTION("positional ordinal") { + auto node = order_by(1); + internal::iterate_ast(node, lambda); + } + SECTION("sole column alias") { + auto node = order_by(get()); + internal::iterate_ast(node, lambda); + } + SECTION("column alias in expression") { + auto node = order_by(get() > c(1)); + expected.push_back(typeid(int)); + internal::iterate_ast(node, lambda); + } } SECTION("group_by") { auto node = group_by(&User::id); diff --git a/tests/statement_serializer_tests/bindables.cpp b/tests/statement_serializer_tests/bindables.cpp index 0ac25bcf6..922cdab2e 100644 --- a/tests/statement_serializer_tests/bindables.cpp +++ b/tests/statement_serializer_tests/bindables.cpp @@ -1,26 +1,92 @@ #include #include #include +#include // std::fill_n #include #include using std::array; +using std::index_sequence; +using std::index_sequence_for; +using std::make_index_sequence; using std::nullptr_t; using std::shared_ptr; using std::string; using std::tuple; +using std::tuple_element_t; +using std::tuple_size; using std::unique_ptr; using std::vector; +using std::wstring; using namespace sqlite_orm; -inline void require_string(const std::string& value, const std::string& expected) { +template +constexpr T get_default() { + return T{}; +} + +template<> +constexpr auto get_default() -> const char* { + return ""; +} + +template<> +constexpr auto get_default() -> const wchar_t* { + return L""; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {""}; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {L""}; +} + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +template<> +constexpr auto get_default() -> std::nullopt_t { + return std::nullopt; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {std::nullopt}; +} +#endif + +template +constexpr Tpl make_default_tuple(index_sequence) { + return {get_default>()...}; +} + +template +constexpr Tpl make_default_tuple() { + return make_default_tuple(make_index_sequence::value>{}); +} + +template +array single_value_array(const char* s) { + array a; + std::fill_n(a.data(), a.size(), s); + return a; +} + +template +struct wrap_in_literal { + using type = internal::literal_holder; +}; + +inline void require_string(const string& value, const string& expected) { REQUIRE(value == expected); } template void require_strings(const array& values, const array& expected, - std::index_sequence) { + index_sequence) { for(size_t i = 0; i < sizeof...(Idx); ++i) { require_string(values[i], expected[i]); } @@ -28,7 +94,7 @@ void require_strings(const array& values, template void test_tuple(const tuple& t, const Ctx& ctx, const array& expected) { - require_strings({internal::serialize(get(t), ctx)...}, expected, std::index_sequence_for{}); + require_strings({internal::serialize(get(t), ctx)...}, expected, index_sequence_for{}); } struct Custom {}; @@ -87,66 +153,54 @@ TEST_CASE("bindables") { #endif >; - Tuple t{false, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0.f, - 0., - 0., - "", - nullptr -#ifndef SQLITE_ORM_OMITS_CODECVT - , - L"" -#endif -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - , - std::nullopt -#endif - }; - array::value> e{"0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "''", - "null" + constexpr Tuple t = make_default_tuple(); + + array::value> e{"0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "''", + "null" #ifndef SQLITE_ORM_OMITS_CODECVT - , - "''" + , + "''" #endif #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - , - "null" + , + "null" #endif }; - test_tuple(t, context, e); + SECTION("dump") { + context.replace_bindable_with_question = false; + test_tuple(t, context, e); + } + SECTION("parametrized") { + context.replace_bindable_with_question = true; + test_tuple(t, context, single_value_array::value>("?")); + } + SECTION("non-bindable literals") { + context.replace_bindable_with_question = true; + constexpr auto t = make_default_tuple::type>(); + test_tuple(t, context, e); + } } SECTION("bindable_types") { using Tuple = tuple, #endif unique_ptr, @@ -164,57 +218,50 @@ TEST_CASE("bindables") { #endif StringVeneer, Custom, - std::unique_ptr>; + unique_ptr>; - Tuple t{"", -#ifndef SQLITE_ORM_OMITS_CODECVT - L"", - L"", -#endif - nullptr, - nullptr, - vector{}, -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - std::nullopt, - std::nullopt, -#endif -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - "", -#ifndef SQLITE_ORM_OMITS_CODECVT - L"", -#endif -#endif - "", - Custom{}, - nullptr}; - array::value> e{"''", + Tuple t = make_default_tuple(); + + array::value> e{"''", #ifndef SQLITE_ORM_OMITS_CODECVT - "''", - "''", + "''", + "''", #endif - "null", - "null", - "x''", + "null", + "null", + "x''", #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - "null", - "null", + "null", + "null", #endif #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - "''", + "''", #ifndef SQLITE_ORM_OMITS_CODECVT - "''", + "''", #endif #endif - "''", - "custom", - "null"}; + "''", + "custom", + "null"}; - test_tuple(t, context, e); + SECTION("dump") { + context.replace_bindable_with_question = false; + test_tuple(t, context, e); + } + SECTION("parametrized") { + context.replace_bindable_with_question = true; + test_tuple(t, context, single_value_array::value>("?")); + } + SECTION("non-bindable literals") { + context.replace_bindable_with_question = true; + auto t = make_default_tuple::type>(); + test_tuple(t, context, e); + } } SECTION("bindable_pointer") { - string value; - decltype(value) expected; + string value, expected; + context.replace_bindable_with_question = false; SECTION("null by itself") { auto v = statically_bindable_pointer(nullptr); diff --git a/tests/statement_serializer_tests/conditions.cpp b/tests/statement_serializer_tests/conditions.cpp index 82a021aae..cdee7dacf 100644 --- a/tests/statement_serializer_tests/conditions.cpp +++ b/tests/statement_serializer_tests/conditions.cpp @@ -4,6 +4,8 @@ using namespace sqlite_orm; TEST_CASE("statement_serializer conditions") { + std::string value, expected; + SECTION("using") { struct User { int64 id; @@ -16,14 +18,28 @@ TEST_CASE("statement_serializer conditions") { internal::serializer_context ctx{storage}; SECTION("using column") { - auto ast = using_(&User::id); - auto value = serialize(ast, ctx); - REQUIRE(value == R"(USING ("id"))"); + auto expression = using_(&User::id); + value = serialize(expression, ctx); + expected = R"(USING ("id"))"; } SECTION("using explicit column") { - auto ast = using_(column(&User::id)); - auto value = serialize(ast, ctx); - REQUIRE(value == R"(USING ("id"))"); + auto expression = using_(column(&User::id)); + value = serialize(expression, ctx); + expected = R"(USING ("id"))"; + } + } + SECTION("order by") { + auto storage = internal::storage_impl<>{}; + using storage_impl = decltype(storage); + + internal::serializer_context ctx{storage}; + + SECTION("positional ordinal") { + auto expression = order_by(1); + value = serialize(expression, ctx); + expected = "ORDER BY 1"; } } + + REQUIRE(value == expected); } diff --git a/tests/static_tests/is_bindable.cpp b/tests/static_tests/is_bindable.cpp index 926eff477..c9aa931f3 100644 --- a/tests/static_tests/is_bindable.cpp +++ b/tests/static_tests/is_bindable.cpp @@ -70,6 +70,7 @@ TEST_CASE("is_bindable") { STATIC_REQUIRE(is_bindable_v>); STATIC_REQUIRE(!is_bindable_v); + STATIC_REQUIRE(!is_bindable_v>); STATIC_REQUIRE(!is_bindable_v); STATIC_REQUIRE(!is_bindable_v>); { diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index c0e1daf48..98c0a0ea2 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -19,306 +19,315 @@ struct is_tuple> : std::true_type {}; TEST_CASE("Node tuple") { using internal::bindable_filter; using internal::node_tuple; + using internal::node_tuple_t; using std::is_same; + using std::tuple; struct User { int id = 0; std::string name; }; - SECTION("simple") { + SECTION("bindables") { SECTION("int") { - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "int"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "bindable int"); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("float") { - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "float"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "bindable float"); + STATIC_REQUIRE(is_same::type, tuple>::value); } } + SECTION("non-bindable literals") { + using namespace internal; + using Tuple = node_tuple_t>; + using Expected = tuple<>; + static_assert(is_same::value, "literal int"); + STATIC_REQUIRE(is_same::type, tuple<>>::value); + } SECTION("binary_condition") { using namespace internal; SECTION("5 < 6.0f") { auto c = lesser_than(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "lesser_than_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("id < 10") { auto c = lesser_than(&User::id, 10); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "lesser_than_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 <= 6.0f") { auto c = lesser_or_equal(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "lesser_or_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("id <= 10.0") { auto c = lesser_or_equal(&User::id, 10.0); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "lesser_or_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 > 6.0f") { auto c = greater_than(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "greater_than_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("id > 20") { auto c = greater_than(&User::id, 20); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "greater_than_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 >= 6.0f") { auto c = greater_or_equal(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "greater_or_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 >= id") { auto c = greater_or_equal(5, &User::id); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "greater_or_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 == 6.0f") { auto c = is_equal(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "is_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("'ototo' == name") { auto c = is_equal("ototo", &User::name); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "is_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("5 != 6.0f") { auto c = is_not_equal(5, 6.0f); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "is_not_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("name != std::string('ototo')") { auto c = is_not_equal(&User::name, std::string("ototo")); using C = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "is_not_equal_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("bool and int") { using Tuple = node_tuple>::type; - using Expected = std::tuple; + using Expected = tuple; static_assert(is_same::value, "and_condition_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } SECTION("bool or int") { using Tuple = node_tuple>::type; - using Expected = std::tuple; + using Expected = tuple; static_assert(is_same::value, "or_condition_t"); - STATIC_REQUIRE(is_same::type, std::tuple>::value); + STATIC_REQUIRE(is_same::type, tuple>::value); } } SECTION("binary_operator") { using namespace internal; using CondTuple = node_tuple>::type; - static_assert(is_same>::value, "conc_t"); + static_assert(is_same>::value, "conc_t"); using AddTuple = node_tuple>::type; - static_assert(is_same>::value, "add_t"); + static_assert(is_same>::value, "add_t"); using SubTuple = node_tuple>::type; - static_assert(is_same>::value, "sub_t"); + static_assert(is_same>::value, "sub_t"); using MulTuple = node_tuple>::type; - static_assert(is_same>::value, "mul_t"); + static_assert(is_same>::value, "mul_t"); using DivTuple = node_tuple>::type; - static_assert(is_same>::value, "div_t"); + static_assert(is_same>::value, "div_t"); using ModTuple = node_tuple>::type; - static_assert(is_same>::value, "mod_t"); + static_assert(is_same>::value, "mod_t"); using AssignTuple = node_tuple>::type; - static_assert(is_same>::value, "assign_t"); + static_assert(is_same>::value, "assign_t"); } SECTION("columns") { auto cols = columns(&User::id, &User::name); using Cols = decltype(cols); - using ColsTuple = node_tuple::type; - static_assert(is_same>::value, "columns_t"); + using ColsTuple = node_tuple_t; + static_assert(is_same>::value, "columns_t"); } SECTION("in") { auto inValue = in(&User::id, {1, 2, 3}); using In = decltype(inValue); - using InTuple = node_tuple::type; - static_assert(is_same>>::value, "in_t"); + using InTuple = node_tuple_t; + static_assert(is_same>>::value, "in_t"); } SECTION("exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { auto c = exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple>; + using Tuple = node_tuple_t; + using Expected = tuple>; static_assert(is_same::value, "exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); } SECTION("aggregate functions") { SECTION("avg") { auto node = avg(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "avg"); } SECTION("avg filter") { auto node = avg(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "avg filter"); } SECTION("count(*)") { auto node = count(); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "count(*)"); } SECTION("count(*) filter") { auto node = count().filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple()), decltype(&User::name), int>; + using Tuple = node_tuple_t; + using Expected = tuple()), decltype(&User::name), int>; static_assert(is_same::value, "count(*) filter"); } SECTION("count(X)") { auto node = count(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "count(X)"); } SECTION("count(X) filter") { auto node = count(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "count(X) filter"); } SECTION("group_concat(X)") { auto node = group_concat(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "group_concat(X)"); } SECTION("group_concat(X) filter") { auto node = group_concat(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "group_concat(X) filter"); } SECTION("group_concat(X,Y)") { auto node = group_concat(&User::id, std::string("-")); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "group_concat(X,Y)"); } SECTION("group_concat(X,Y) filter") { auto node = group_concat(&User::id, std::string("-")).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "group_concat(X,Y) filter"); } SECTION("max(X)") { auto node = max(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "max(X)"); } SECTION("max(X) filter") { auto node = max(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "max(X) filter"); } SECTION("min(X)") { auto node = min(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "min(X)"); } SECTION("min(X) filter") { auto node = min(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "min(X) filter"); } SECTION("sum(X)") { auto node = sum(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "sum(X)"); } SECTION("sum(X) filter") { auto node = sum(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "sum(X) filter"); } SECTION("total(X)") { auto node = total(&User::id); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "total(X)"); } SECTION("total(X) filter") { auto node = total(&User::id).filter(where(length(&User::name) > 5)); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "total(X) filter"); } } @@ -326,15 +335,15 @@ TEST_CASE("Node tuple") { SECTION("max(X,Y)") { auto node = max(&User::id, 4); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "max(X,Y)"); } SECTION("min(X,Y)") { auto node = min(&User::id, 4); using Node = decltype(node); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "min(X,Y)"); } } @@ -342,43 +351,42 @@ TEST_CASE("Node tuple") { SECTION("union_(select(1), select(2))") { auto un = union_(select(1), select(2)); using Union = decltype(un); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "union_(select(1), select(2))"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "union_(select(1), select(2))"); } SECTION("union_all") { auto un = union_all(select(&User::id, where(is_equal(&User::name, "Alice"))), select(&User::id, where(is_equal(std::string("Bob"), &User::name)))); using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "union_all"); } SECTION("except") { auto un = except(select(columns(&User::id, &User::name), where(is_equal(&User::id, 10L))), select(columns(&User::id, &User::name), where(is_equal(&User::id, 15L)))); using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "except"); } SECTION("intersect") { auto un = intersect(select(&User::name), select(&User::name, where(is_equal(&User::name, "Anny")))); using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = - std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "intersect"); } } @@ -386,67 +394,67 @@ TEST_CASE("Node tuple") { auto expression = replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } SECTION("insert_raw_t") { auto expression = insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } SECTION("tuple") { auto expression = std::make_tuple(1, std::string("hi")); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } SECTION("values") { SECTION("int + string") { auto expression = values(1, std::string("hi")); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } SECTION("tuple") { auto expression = values(std::make_tuple(1, std::string("hi"))); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } } SECTION("into") { auto expression = into(); using Expression = decltype(expression); - using Tuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); } SECTION("select") { SECTION("select(&User::id)") { auto sel = select(&User::id); using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "select(&User::id)"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "select(&User::id)"); } SECTION("select(&User::name)") { auto sel = select(&User::name); using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "select(&User::name)"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "select(&User::name)"); } SECTION("select(&User::id, where(is_equal(&User::id, 5)))") { auto sel = select(&User::id, where(is_equal(&User::id, 5))); using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, + using Tuple = node_tuple_t; + static_assert(is_same>::value, "select(&User::id, where(is_equal(&User::id, 5)))"); } SECTION("select(&User::name, where(lesser_than(&User::id, 10)))") { auto sel = select(&User::name, where(lesser_than(&User::id, 10))); using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, + using Tuple = node_tuple_t; + static_assert(is_same>::value, "select(&User::name, where(lesser_than(&User::id, 10)))"); } SECTION("select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and " @@ -454,7 +462,7 @@ TEST_CASE("Node tuple") { auto sel = select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and lesser_or_equal(&User::id, 20))); using Sel = decltype(sel); - using Tuple = node_tuple::type; + using Tuple = node_tuple_t; using Expected = std:: tuple; static_assert(is_same::value, @@ -464,8 +472,8 @@ TEST_CASE("Node tuple") { SECTION("select(columns('ototo', 25))") { auto statement = select(columns("ototo", 25)); using Statement = decltype(statement); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } } @@ -473,51 +481,50 @@ TEST_CASE("Node tuple") { SECTION("get_all()") { auto getAll = get_all(); using GetAll = decltype(getAll); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "get_all()"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "get_all()"); } SECTION("get_all(where(is_equal(5.0, &User::id)))") { auto getAll = get_all(where(is_equal(5.0, &User::id))); using GetAll = decltype(getAll); - using Tuple = node_tuple::type; - static_assert(is_same>::value, + using Tuple = node_tuple_t; + static_assert(is_same>::value, "get_all(where(is_equal(5.0, &User::id)))"); } SECTION("get_all(where(is_equal(5.0, &User::id)))") { auto getAll = get_all(where(is_equal(&User::id, 1) or is_equal(std::string("Alex"), &User::name))); using GetAll = decltype(getAll); - using Tuple = node_tuple::type; - static_assert( - is_same>::value, - "get_all(where(is_equal(5.0, &User::id)))"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, + "get_all(where(is_equal(5.0, &User::id)))"); } } SECTION("having_t") { using namespace internal; auto hav = having(greater_or_equal(&User::id, 10)); using Having = decltype(hav); - using Tuple = node_tuple::type; - static_assert(is_same>::value, + using Tuple = node_tuple_t; + static_assert(is_same>::value, "having(greater_or_equal(&User::id, 10))"); } SECTION("cast_t") { auto sel = select(columns(cast(&User::id), cast(&User::name))); using Select = decltype(sel); - using Tuple = node_tuple; + static_assert(is_same>::value, "select(columns(cast(&User::id), cast(&User::name)))"); } SECTION("optional_container") { using namespace internal; SECTION("int") { using Op = optional_container; - using Tuple = node_tuple::type; - static_assert(is_same>::value, "optional_container"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "optional_container"); } SECTION("void") { using Op = optional_container; - using Tuple = node_tuple::type; - static_assert(is_same>::value, "optional_container"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "optional_container"); } } SECTION("like_t") { @@ -526,137 +533,154 @@ TEST_CASE("Node tuple") { using Like = decltype(lk); using NodeTuple = node_tuple; using ArgTuple = NodeTuple::arg_tuple; - static_assert(is_same>::value, "arg_tuple"); + static_assert(is_same>::value, "arg_tuple"); using PatternTuple = NodeTuple::pattern_tuple; - static_assert(is_same>::value, "pattern_tuple"); + static_assert(is_same>::value, "pattern_tuple"); using EscapeTuple = NodeTuple::escape_tuple; - static_assert(is_same>::value, "escape_tuple"); + static_assert(is_same>::value, "escape_tuple"); using Tuple = NodeTuple::type; static_assert(std::tuple_size::value == 2, "like(&User::name, \"S%\") size"); - using Tuple0 = std::tuple_element<0, Tuple>::type; + using Tuple0 = std::tuple_element_t<0, Tuple>; static_assert(is_same::value, "like(&User::name, \"S%\") type 0"); - static_assert(is_same>::value, + static_assert(is_same>::value, "like(&User::name, \"S%\")"); } SECTION("like(&User::name, std::string('pattern'), '%')") { auto lk = like(&User::name, std::string("pattern"), "%"); using Like = decltype(lk); - using NodeTuple = node_tuple::type; - using Expected = std::tuple; + using NodeTuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "like(&User::name, std::string(\"pattern\"), \"%\")"); } SECTION("like(&User::name, std::string('pattern')).escape('%')") { auto lk = like(&User::name, std::string("pattern")).escape("%"); using Like = decltype(lk); - using NodeTuple = node_tuple::type; - using Expected = std::tuple; + using NodeTuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "like(&User::name, std::string(\"pattern\")).escape(\"%\")"); } } + SECTION("order_by_t") { + SECTION("expression") { + STATIC_REQUIRE(is_same, + tuple>::value); + } + SECTION("bindable") { + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("positional ordinal") { + STATIC_REQUIRE(is_same, tuple<>>::value); + } + SECTION("sole column alias") { + STATIC_REQUIRE(is_same()))>, tuple<>>::value); + } + SECTION("column alias in expression") { + STATIC_REQUIRE(is_same() > c(1)))>, tuple>::value); + } + } SECTION("glob_t") { auto gl = glob(&User::name, "H*"); using Glob = decltype(gl); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "glob(&User::name, \"H*\")"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "glob(&User::name, \"H*\")"); } SECTION("between_t") { auto bet = between(&User::id, 10, 20); using Between = decltype(bet); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "between(&User::id, 10, 20)"); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "between(&User::id, 10, 20)"); } SECTION("named_collate") { auto sel = select(&User::name, where(is_equal(&User::name, "Mercury").collate("ototo"))); using Select = decltype(sel); - using Tuple = node_tuple; + using Expected = tuple; static_assert(is_same::value, "named_collate"); } SECTION("negated_condition_t") { SECTION("not is_equal(20, '20')") { auto c = not is_equal(20, "20"); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not is_equal(20, \"20\")"); } SECTION("not is_not_equal(&User::id, 15.0)") { auto c = not is_not_equal(&User::id, 15.0); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not is_not_equal(&User::id, 15.0)"); } SECTION("not greater_than(20.0f, &User::id)") { auto c = not greater_than(20.0f, &User::id); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not greater_than(20.0f, &User::id)"); } SECTION("not greater_or_equal(&User::id, 5)") { auto c = not greater_or_equal(&User::id, 5); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not greater_or_equal(&User::id, 5)"); } SECTION("not lesser_than(&User::id, std::string('6'))") { auto c = not lesser_than(&User::id, std::string("6")); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not lesser_than(&User::id, std::string(\"6\"))"); } SECTION("not lesser_or_equal(&User::id, 10)") { auto c = not lesser_or_equal(&User::id, 10); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not lesser_or_equal(&User::id, 10)"); } SECTION("not in(&User::id, {1, 2, 3})") { auto c = not in(&User::id, {1, 2, 3}); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple>; + using Tuple = node_tuple_t; + using Expected = tuple>; static_assert(is_same::value, "not in(&User::id, {1, 2, 3})"); } SECTION("not is_null(&User::name)") { auto c = not is_null(&User::name); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not is_null(&User::name)"); } SECTION("not is_not_null(&User::name)") { auto c = not is_not_null(&User::name); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not is_not_null(&User::name)"); } SECTION("not like(&User::name, '*D*')") { auto c = not like(&User::name, "*D*"); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not like(&User::name, \"*D*\")"); } SECTION("not glob(&User::name, std::string('_A_'))") { auto c = not glob(&User::name, std::string("_A_")); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "not glob(&User::name, std::string(\"_A_\"))"); } SECTION("not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { auto c = not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple>; + using Tuple = node_tuple_t; + using Expected = tuple>; static_assert(is_same::value, "not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); } @@ -665,132 +689,132 @@ TEST_CASE("Node tuple") { SECTION("lower") { auto f = lower(&User::name); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "lower"); } SECTION("upper") { auto f = upper("hi"); using Fun = decltype(f); - using Tuple = node_tuple::type; - using ArgType = std::tuple_element<0, Fun::args_type>::type; + using Tuple = node_tuple_t; + using ArgType = std::tuple_element_t<0, Fun::args_type>; static_assert(is_same::value, "upper arg[0]"); - using Expected = std::tuple; + using Expected = tuple; static_assert(is_same::value, "upper"); } SECTION("total_changes") { auto f = total_changes(); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple<>; + using Tuple = node_tuple_t; + using Expected = tuple<>; static_assert(is_same::value, "total_changes"); } SECTION("changes") { auto f = changes(); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple<>; + using Tuple = node_tuple_t; + using Expected = tuple<>; static_assert(is_same::value, "changes"); } SECTION("trim(1)") { auto f = trim(&User::name); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "trim(1)"); } SECTION("trim(2)") { auto f = trim(&User::name, std::string("pay")); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "trim(2)"); } SECTION("ltrim(1)") { auto f = ltrim(&User::id); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "ltrim(1)"); } SECTION("ltrim(2)") { auto f = ltrim(&User::id, "see"); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "ltrim(2)"); } SECTION("rtrim(1)") { auto f = rtrim(&User::name); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "rtrim(1)"); } SECTION("rtrim(2)") { auto f = rtrim(&User::name, &User::id); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "rtrim(2)"); } #if SQLITE_VERSION_NUMBER >= 3007016 SECTION("char_") { auto f = char_(100, 20.0); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "char_"); } #endif SECTION("coalesce") { auto f = char_(10, 20); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "coalesce"); } SECTION("date") { auto f = date(std::string("now"), std::string("start of month"), std::string("+1 month"), std::string("-1 day")); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "date"); } SECTION("datetime") { auto f = datetime("now"); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "datetime"); } SECTION("julianday") { auto f = julianday("now"); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "julianday"); } SECTION("zeroblob") { auto f = zeroblob(10); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "zeroblob"); } SECTION("substr(2)") { auto f = substr(&User::name, 7); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "substr"); } SECTION("substr(3)") { auto f = substr(&User::name, 7, 20.f); using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "substr"); } } @@ -798,44 +822,44 @@ TEST_CASE("Node tuple") { SECTION("left_join") { auto j = left_join(on(is_equal(&User::id, 2))); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "left_join"); } SECTION("join on") { auto j = join(on(is_equal(&User::id, 2))); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "join on"); } SECTION("join using column") { auto j = join(using_(&User::id)); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple>; + using Tuple = node_tuple_t; + using Expected = tuple>; static_assert(is_same::value, "join using"); } SECTION("join using explicit column") { struct Derived : User {}; auto j = join(using_(column(&User::id))); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple>; + using Tuple = node_tuple_t; + using Expected = tuple>; static_assert(is_same::value, "join using explicit column"); } SECTION("left_outer_join") { auto j = left_outer_join(on(is_equal(&User::id, 2))); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "left_outer_join"); } SECTION("inner_join") { auto j = inner_join(on(is_equal(&User::id, 2))); using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; + using Tuple = node_tuple_t; + using Expected = tuple; static_assert(is_same::value, "inner_join"); } } @@ -843,10 +867,10 @@ TEST_CASE("Node tuple") { auto c = case_(&User::name).when("USA", then("Dosmetic")).else_("Foreign").end(); using Case = decltype(c); using CaseExpressionTuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + STATIC_REQUIRE(is_same>::value); - STATIC_REQUIRE(is_tuple>::value); - STATIC_REQUIRE(is_tuple>::value); + STATIC_REQUIRE(is_tuple>::value); + STATIC_REQUIRE(is_tuple>::value); STATIC_REQUIRE(!is_tuple::value); STATIC_REQUIRE(is_pair>::value); STATIC_REQUIRE(!is_pair::value); @@ -855,16 +879,16 @@ TEST_CASE("Node tuple") { STATIC_REQUIRE(is_tuple::value); STATIC_REQUIRE(std::tuple_size::value == 1); - using Arg0 = std::tuple_element<0, ArgsType>::type; + using Arg0 = std::tuple_element_t<0, ArgsType>; STATIC_REQUIRE(is_pair::value); using Arg0First = Arg0::first_type; STATIC_REQUIRE(is_same::value); using Arg0Second = Arg0::second_type; STATIC_REQUIRE(is_same::value); - STATIC_REQUIRE(is_same>>::value); + STATIC_REQUIRE(is_same>>::value); using ElseExpressionTuple = node_tuple::type; - STATIC_REQUIRE(is_same>::value); + STATIC_REQUIRE(is_same>::value); } SECTION("as") { struct GradeAlias : alias_tag { @@ -875,8 +899,8 @@ TEST_CASE("Node tuple") { }; auto a = as(&User::name); using A = decltype(a); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(is_same::value); } SECTION("function_call") { @@ -887,29 +911,29 @@ TEST_CASE("Node tuple") { }; auto statement = func(8); using Statement = decltype(statement); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } SECTION("excluded") { auto statement = excluded(&User::id); using Statement = decltype(statement); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } SECTION("upsert_clause") { auto statement = on_conflict(&User::id).do_update(set(c(&User::name) = excluded(&User::name))); using Statement = decltype(statement); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } SECTION("group_by") { auto statement = group_by(&User::id); using Statement = decltype(statement); - using Tuple = node_tuple::type; - using ExpectedTuple = std::tuple; + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } }