diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..f94da0a4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,53 @@ +Language: Cpp +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: false +ColumnLimit: 160 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +IndentWrappedFunctionNames: false +IndentFunctionDeclarationAfterType: false +MaxEmptyLinesToKeep: 1 +KeepEmptyLinesAtTheStartOfBlocks: false +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +SpacesBeforeTrailingComments: 2 +Cpp11BracedListStyle: true +Standard: Cpp11 +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +BreakBeforeBraces: Allman +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 +CommentPragmas: '^ IWYU pragma:' +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +SpaceBeforeParens: ControlStatements +DisableFormat: false diff --git a/CMakeLists.txt b/CMakeLists.txt index b1116b7f..69ca76f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall ${CMAKE_CXX_FLAGS}") endif () +set(CMAKE_CXX_FLAGS "-Wconversion -Wpedantic ${CMAKE_CXX_FLAGS}") set(include_dir "${PROJECT_SOURCE_DIR}/include") file(GLOB_RECURSE sqlpp_headers "${include_dir}/*.h") diff --git a/examples/select.cpp b/examples/select.cpp index 8b4317c2..790fd96b 100644 --- a/examples/select.cpp +++ b/examples/select.cpp @@ -37,7 +37,7 @@ static constexpr bool some_condition = true; static constexpr bool some_other_condition = false; -SQLPP_ALIAS_PROVIDER(cheesecake); +SQLPP_ALIAS_PROVIDER(cheesecake) MockDb db; @@ -110,7 +110,7 @@ int main() .from(p.join(x).on(p.feature == x.cheesecake)) .where(true))) { - int id = row.id; + int64_t id = row.id; std::string name = row.name; std::string x_name = row.x.name; int cheesecake = row.x.cheesecake; diff --git a/include/sqlpp11/alias_provider.h b/include/sqlpp11/alias_provider.h index 266ec51e..57d501ca 100644 --- a/include/sqlpp11/alias_provider.h +++ b/include/sqlpp11/alias_provider.h @@ -62,35 +62,35 @@ namespace sqlpp static constexpr bool value = true; }; - namespace alias + inline namespace alias { - SQLPP_ALIAS_PROVIDER(a); - SQLPP_ALIAS_PROVIDER(b); - SQLPP_ALIAS_PROVIDER(c); - SQLPP_ALIAS_PROVIDER(d); - SQLPP_ALIAS_PROVIDER(e); - SQLPP_ALIAS_PROVIDER(f); - SQLPP_ALIAS_PROVIDER(g); - SQLPP_ALIAS_PROVIDER(h); - SQLPP_ALIAS_PROVIDER(i); - SQLPP_ALIAS_PROVIDER(j); - SQLPP_ALIAS_PROVIDER(k); - SQLPP_ALIAS_PROVIDER(l); - SQLPP_ALIAS_PROVIDER(m); - SQLPP_ALIAS_PROVIDER(n); - SQLPP_ALIAS_PROVIDER(o); - SQLPP_ALIAS_PROVIDER(p); - SQLPP_ALIAS_PROVIDER(q); - SQLPP_ALIAS_PROVIDER(s); - SQLPP_ALIAS_PROVIDER(t); - SQLPP_ALIAS_PROVIDER(u); - SQLPP_ALIAS_PROVIDER(v); - SQLPP_ALIAS_PROVIDER(w); - SQLPP_ALIAS_PROVIDER(x); - SQLPP_ALIAS_PROVIDER(y); - SQLPP_ALIAS_PROVIDER(z); - SQLPP_ALIAS_PROVIDER(left); - SQLPP_ALIAS_PROVIDER(right); + SQLPP_ALIAS_PROVIDER(a) + SQLPP_ALIAS_PROVIDER(b) + SQLPP_ALIAS_PROVIDER(c) + SQLPP_ALIAS_PROVIDER(d) + SQLPP_ALIAS_PROVIDER(e) + SQLPP_ALIAS_PROVIDER(f) + SQLPP_ALIAS_PROVIDER(g) + SQLPP_ALIAS_PROVIDER(h) + SQLPP_ALIAS_PROVIDER(i) + SQLPP_ALIAS_PROVIDER(j) + SQLPP_ALIAS_PROVIDER(k) + SQLPP_ALIAS_PROVIDER(l) + SQLPP_ALIAS_PROVIDER(m) + SQLPP_ALIAS_PROVIDER(n) + SQLPP_ALIAS_PROVIDER(o) + SQLPP_ALIAS_PROVIDER(p) + SQLPP_ALIAS_PROVIDER(q) + SQLPP_ALIAS_PROVIDER(s) + SQLPP_ALIAS_PROVIDER(t) + SQLPP_ALIAS_PROVIDER(u) + SQLPP_ALIAS_PROVIDER(v) + SQLPP_ALIAS_PROVIDER(w) + SQLPP_ALIAS_PROVIDER(x) + SQLPP_ALIAS_PROVIDER(y) + SQLPP_ALIAS_PROVIDER(z) + SQLPP_ALIAS_PROVIDER(left) + SQLPP_ALIAS_PROVIDER(right) } } diff --git a/include/sqlpp11/column.h b/include/sqlpp11/column.h index 82d34183..83468e5b 100644 --- a/include/sqlpp11/column.h +++ b/include/sqlpp11/column.h @@ -54,11 +54,13 @@ namespace sqlpp struct _recursive_traits { - using _parameters = std::tuple<>; + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set; using _provided_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set
; using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; using _tags = typename std::conditional::value, detail::type_set, detail::type_set<>>::type; diff --git a/include/sqlpp11/count.h b/include/sqlpp11/count.h index ffe74bc4..1403aa8b 100644 --- a/include/sqlpp11/count.h +++ b/include/sqlpp11/count.h @@ -41,6 +41,8 @@ namespace sqlpp using _traits = make_traits; struct _recursive_traits { + using _required_ctes = required_ctes_of; + using _provided_ctes = detail::type_set<>; using _required_tables = required_tables_of; using _provided_tables = provided_tables_of; using _provided_outer_tables = provided_outer_tables_of; diff --git a/include/sqlpp11/cte.h b/include/sqlpp11/cte.h new file mode 100644 index 00000000..f84f3816 --- /dev/null +++ b/include/sqlpp11/cte.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_CTE_H +#define SQLPP_CTE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sqlpp +{ + template + struct cte_union_t + { + struct _recursive_traits + { + using _required_ctes = detail::make_joined_set_t, required_ctes_of>; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set<>; + using _provided_tables = detail::type_set<>; + using _provided_outer_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _parameters = detail::make_parameter_tuple_t, parameters_of>; + using _tags = detail::type_set<>; + }; + + cte_union_t(Lhs lhs, Rhs rhs): + _lhs(lhs), + _rhs(rhs) + {} + + cte_union_t(const cte_union_t&) = default; + cte_union_t(cte_union_t&&) = default; + cte_union_t& operator=(const cte_union_t&) = default; + cte_union_t& operator=(cte_union_t&&) = default; + ~cte_union_t() = default; + + Lhs _lhs; + Rhs _rhs; + }; + + // Interpreters + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = cte_union_t; + + static Context& _(const T& t, Context& context) + { + context << '('; + serialize(t._lhs, context); + context << ") UNION "; + serialize(Flag{}, context); + context << " ("; + serialize(t._rhs, context); + context << ')'; + return context; + } + }; + + template + struct cte_t; + + template + struct cte_ref_t; + + template + auto from_table(cte_t t) -> cte_ref_t + { + return cte_ref_t{}; + } + + template + struct from_table_impl> + { + using type = cte_ref_t; + }; + + + template + struct cte_column_spec_t + { + using _alias_t = typename FieldSpec::_alias_t; + + using _traits = make_traits, + tag::must_not_insert, + tag::must_not_update, + tag_if::value> + >; + }; + + template + struct make_cte_impl + { + using type = void; + }; + + template + struct make_cte_impl> + { + using type = cte_t; + }; + + template + using make_cte_t = typename make_cte_impl>::type; + + template + struct cte_t: public member_t, column_t>>... + { + using _traits = make_traits; // FIXME: is table? really? + struct _recursive_traits + { + using _required_ctes = detail::make_joined_set_t, detail::type_set>; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set<>; + using _provided_tables = detail::type_set<>; + using _provided_outer_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _parameters = parameters_of; + using _tags = detail::type_set<>; + }; + using _alias_t = typename AliasProvider::_alias_t; + constexpr static bool _is_recursive = detail::is_element_of>::value; + + using _column_tuple_t = std::tuple>...>; + + template + using _check = logic::all_t::value...>; + + using _result_row_t = result_row_t; + + template + auto union_distinct(Rhs rhs) const + -> typename std::conditional<_check::value, cte_t, FieldSpecs...>, bad_statement>::type + { + static_assert(is_statement_t::value, "argument of union call has to be a statement"); + static_assert(has_policy_t::value, "argument of union call has to be a select"); + static_assert(has_result_row_t::value, "argument of a union has to be a (complete) select statement"); + + static_assert(std::is_same<_result_row_t, get_result_row_t>::value, "both select statements in a union have to have the same result columns (type and name)"); + + return _union_impl(_check{}, rhs); + } + + template + auto union_all(Rhs rhs) const + -> typename std::conditional<_check::value, cte_t, FieldSpecs...>, bad_statement>::type + { + static_assert(is_statement_t::value, "argument of union call has to be a statement"); + static_assert(has_policy_t::value, "argument of union call has to be a select"); + static_assert(has_result_row_t::value, "argument of a union has to be a (complete) select statement"); + + static_assert(std::is_same<_result_row_t, get_result_row_t>::value, "both select statements in a union have to have the same result columns (type and name)"); + + return _union_impl(_check{}, rhs); + } + + private: + template + auto _union_impl(const std::false_type&, Rhs rhs) const + -> bad_statement; + + template + auto _union_impl(const std::true_type&, Rhs rhs) const + -> cte_t, FieldSpecs...> + { + return cte_union_t{_statement, rhs}; + } + + public: + + cte_t(Statement statement): _statement(statement){} + cte_t(const cte_t&) = default; + cte_t(cte_t&&) = default; + cte_t& operator=(const cte_t&) = default; + cte_t& operator=(cte_t&&) = default; + ~cte_t() = default; + + Statement _statement; + }; + + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = cte_t; + + static Context& _(const T& t, Context& context) + { + context << name_of::char_ptr() << " AS ("; + serialize(t._statement, context); + context << ")"; + return context; + } + }; + + +// The cte_t is displayed as AliasProviderName except within the with: +// - the with needs the +// AliasProviderName AS (ColumnNames) (select/union) +// The result row of the select should not have dynamic parts + template + struct cte_ref_t + { + using _traits = make_traits; // FIXME: is table? really? + struct _recursive_traits + { + using _required_ctes = detail::make_type_set_t; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set<>; + using _provided_tables = detail::type_set; + using _provided_outer_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; + using _tags = detail::type_set<>; + }; + + using _alias_t = typename AliasProvider::_alias_t; + + template + auto as(Statement statement) + -> make_cte_t + { + static_assert(required_tables_of::size::value == 0, "common table expression must not use unknown tables"); + static_assert(not detail::is_element_of>::value, "common table expression must not self-reference in the first part, use union_all/union_distinct for recursion"); + static_assert(is_static_result_row_t>::value, "ctes must not have dynamically added columns"); + + return { statement }; + } + }; + + template + struct serializer_t> + { + using _serialize_check = consistent_t; + using T = cte_ref_t; + + static Context& _(const T& t, Context& context) + { + context << name_of::char_ptr(); + return context; + } + }; + + template + auto cte(const AliasProvider&) + -> cte_ref_t + { + return {}; + } + +} + +#endif diff --git a/include/sqlpp11/detail/pick_arg.h b/include/sqlpp11/detail/pick_arg.h index 10cd512b..6c922516 100644 --- a/include/sqlpp11/detail/pick_arg.h +++ b/include/sqlpp11/detail/pick_arg.h @@ -37,20 +37,20 @@ namespace sqlpp typename Target::_data_t pick_arg_impl(Statement statement, Term term, const std::true_type&) { return term; - }; + } template typename Target::_data_t pick_arg_impl(Statement statement, Term term, const std::false_type&) { return Target::_get_member(statement)._data; - }; + } // Returns a statement's term either by picking the term from the statement or using the new term template typename Target::_data_t pick_arg(Statement statement, Term term) { return pick_arg_impl(statement, term, std::is_same()); - }; + } } } diff --git a/include/sqlpp11/extra_tables.h b/include/sqlpp11/extra_tables.h index 884e1946..cd4e1068 100644 --- a/include/sqlpp11/extra_tables.h +++ b/include/sqlpp11/extra_tables.h @@ -54,11 +54,13 @@ namespace sqlpp using _traits = make_traits; struct _recursive_traits { - using _parameters = std::tuple<>; + using _required_ctes = detail::make_joined_set_t...>; + using _provided_ctes = detail::type_set<>; using _required_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; using _provided_tables = detail::type_set<>; using _extra_tables = detail::type_set; + using _parameters = std::tuple<>; using _tags = detail::type_set<>; }; diff --git a/include/sqlpp11/from.h b/include/sqlpp11/from.h index bbe7d939..37f5b68a 100644 --- a/include/sqlpp11/from.h +++ b/include/sqlpp11/from.h @@ -27,6 +27,7 @@ #ifndef SQLPP_FROM_H #define SQLPP_FROM_H +#include #include #include #include @@ -90,7 +91,7 @@ namespace sqlpp template void _add_impl(Table table, const std::true_type&) { - return _data._dynamic_tables.emplace_back(table); + return _data._dynamic_tables.emplace_back(from_table(table)); } template @@ -164,7 +165,7 @@ namespace sqlpp template auto from(Tables... tables) const - -> _new_statement_t<_check, from_t> + -> _new_statement_t<_check, from_t...>> { static_assert(_check::value, "at least one argument is not a table or join in from()"); static_assert(sizeof...(Tables), "at least one table or join argument required in from()"); @@ -173,7 +174,7 @@ namespace sqlpp template auto dynamic_from(Tables... tables) const - -> _new_statement_t<_check, from_t<_database_t, Tables...>> + -> _new_statement_t<_check, from_t<_database_t, from_table_t...>> { static_assert(not std::is_same<_database_t, void>::value, "dynamic_from must not be called in a static statement"); static_assert(_check::value, "at least one argument is not a table or join in from()"); @@ -187,7 +188,7 @@ namespace sqlpp template auto _from_impl(const std::true_type&, Tables... tables) const - -> _new_statement_t> + -> _new_statement_t...>> { static_assert(required_tables_of>::size::value == 0, "at least one table depends on another table in from()"); @@ -197,7 +198,7 @@ namespace sqlpp static_assert(_number_of_tables == _unique_tables::size::value, "at least one duplicate table detected in from()"); static_assert(_number_of_tables == _unique_table_names::size::value, "at least one duplicate table name detected in from()"); - return { static_cast&>(*this), from_data_t{tables...} }; + return { static_cast&>(*this), from_data_t...>{from_table(tables)...} }; } }; diff --git a/include/sqlpp11/group_by.h b/include/sqlpp11/group_by.h index a8f73185..52c2d53d 100644 --- a/include/sqlpp11/group_by.h +++ b/include/sqlpp11/group_by.h @@ -211,7 +211,7 @@ namespace sqlpp static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in group_by()"); return { static_cast&>(*this), group_by_data_t{expressions...} }; - }; + } }; }; diff --git a/include/sqlpp11/insert_value_list.h b/include/sqlpp11/insert_value_list.h index a0763799..1cc86f62 100644 --- a/include/sqlpp11/insert_value_list.h +++ b/include/sqlpp11/insert_value_list.h @@ -315,7 +315,7 @@ namespace sqlpp static void _() { static_assert(wrong_t::value, "insert values required, e.g. set(...) or default_values()"); - }; + } }; // NO INSERT COLUMNS/VALUES YET diff --git a/include/sqlpp11/interpret_tuple.h b/include/sqlpp11/interpret_tuple.h index 36c047b7..a51a8288 100644 --- a/include/sqlpp11/interpret_tuple.h +++ b/include/sqlpp11/interpret_tuple.h @@ -54,7 +54,9 @@ namespace sqlpp // See also: "http://stackoverflow.com/questions/6245735/pretty-print-stdtuple/6245777#6245777" // Beware of gcc-bug: "http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253", otherwise an empty swallow struct could be used using swallow = int[]; - (void) swallow{(interpret_tuple_element(std::get(t), separator, context, useBraces, Is), 0)...}; + (void) swallow{ + 0, //workaround against -Wpedantic GCC warning "zero-size array 'int [0]'" + (interpret_tuple_element(std::get(t), separator, context, useBraces, Is), 0)...}; return context; } diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index 99dbcdd0..e4dcb97f 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -68,6 +68,8 @@ namespace sqlpp using _traits = make_traits; struct _recursive_traits { + using _required_ctes = detail::make_joined_set_t, required_ctes_of>; + using _provided_ctes = detail::type_set<>; using _required_tables = detail::make_joined_set_t, required_tables_of>; using _provided_tables = detail::make_joined_set_t, provided_tables_of>; using _provided_outer_tables = typename JoinType::template _provided_outer_tables; diff --git a/include/sqlpp11/order_by.h b/include/sqlpp11/order_by.h index daf50517..32f7a0b8 100644 --- a/include/sqlpp11/order_by.h +++ b/include/sqlpp11/order_by.h @@ -211,7 +211,7 @@ namespace sqlpp static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in order_by()"); return { static_cast&>(*this), order_by_data_t{expressions...} }; - }; + } }; }; diff --git a/include/sqlpp11/parameter.h b/include/sqlpp11/parameter.h index b5daaabc..542a5cb1 100644 --- a/include/sqlpp11/parameter.h +++ b/include/sqlpp11/parameter.h @@ -40,11 +40,13 @@ namespace sqlpp using _traits = make_traits; struct _recursive_traits { - using _parameters = std::tuple; + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set<>; using _provided_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; + using _parameters = std::tuple; using _tags = detail::type_set; }; diff --git a/include/sqlpp11/result_field_methods.h b/include/sqlpp11/result_field_methods.h index 5829ba91..4ab50af9 100644 --- a/include/sqlpp11/result_field_methods.h +++ b/include/sqlpp11/result_field_methods.h @@ -83,11 +83,13 @@ namespace sqlpp struct _recursive_traits { - using _parameters = std::tuple<>; + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::type_set<>; + using _required_tables = detail::type_set<>; using _provided_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; using _tags = typename std::conditional::value, detail::type_set, detail::type_set<>>::type; diff --git a/include/sqlpp11/result_row.h b/include/sqlpp11/result_row.h index f9e21fcf..67574354 100644 --- a/include/sqlpp11/result_row.h +++ b/include/sqlpp11/result_row.h @@ -28,6 +28,7 @@ #define SQLPP_RESULT_ROW_H #include +#include #include #include #include @@ -265,6 +266,21 @@ namespace sqlpp } } }; + + template + struct is_static_result_row_impl + { + using type = std::false_type; + }; + + template + struct is_static_result_row_impl> + { + using type = std::true_type; + }; + + template + using is_static_result_row_t = typename is_static_result_row_impl::type; } #endif diff --git a/include/sqlpp11/result_row_fwd.h b/include/sqlpp11/result_row_fwd.h new file mode 100644 index 00000000..8df5b4ed --- /dev/null +++ b/include/sqlpp11/result_row_fwd.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_RESULT_ROW_FWD_H +#define SQLPP_RESULT_ROW_FWD_H + +namespace sqlpp +{ + template + struct result_row_t; + + template + struct dynamic_result_row_t; +} + +#endif diff --git a/include/sqlpp11/select.h b/include/sqlpp11/select.h index b663c7d4..89506245 100644 --- a/include/sqlpp11/select.h +++ b/include/sqlpp11/select.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -69,6 +70,7 @@ namespace sqlpp template using blank_select_t = statement_t _alias_t as(const AliasProvider& aliasProvider) const { + consistency_check_t<_statement_t>::_(); static_assert(_statement_t::_can_be_used_as_table(), "statement cannot be used as table, e.g. due to missing tables"); static_assert(logic::none_t::value...>::value, "cannot use multi-columns in sub selects"); return _table_t(_get_statement()).as(aliasProvider); @@ -340,7 +341,7 @@ namespace sqlpp auto tuple_merge(Columns... columns) -> decltype(std::tuple_cat(as_tuple::_(columns)...)) { return std::tuple_cat(as_tuple::_(columns)...); - }; + } template using make_select_column_list_t = diff --git a/include/sqlpp11/select_pseudo_table.h b/include/sqlpp11/select_pseudo_table.h index 7e50d87a..c4dbd3b4 100644 --- a/include/sqlpp11/select_pseudo_table.h +++ b/include/sqlpp11/select_pseudo_table.h @@ -31,6 +31,8 @@ namespace sqlpp { + // FIXME: We might use field specs here (same as with cte) + // // provide type information for sub-selects that are used as named expressions or tables template struct select_column_spec_t diff --git a/include/sqlpp11/statement.h b/include/sqlpp11/statement.h index 03bb8b35..209a62e0 100644 --- a/include/sqlpp11/statement.h +++ b/include/sqlpp11/statement.h @@ -44,6 +44,17 @@ namespace sqlpp template struct statement_t; + struct assert_no_unknown_ctes_t + { + using type = std::false_type; + + template + static void _() + { + static_assert(wrong_t::value, "one clause requires common table expressions which are otherwise not known in the statement"); + } + }; + struct assert_no_unknown_tables_t { using type = std::false_type; @@ -84,6 +95,8 @@ namespace sqlpp template using _new_statement_t = typename _policies_update_t::type; + using _all_required_ctes = detail::make_joined_set_t...>; + using _all_provided_ctes = detail::make_joined_set_t...>; using _all_required_tables = detail::make_joined_set_t...>; using _all_provided_tables = detail::make_joined_set_t...>; using _all_provided_outer_tables = detail::make_joined_set_t...>; @@ -94,12 +107,21 @@ namespace sqlpp template using _no_unknown_tables = detail::is_subset_of, _known_tables>; + template class Predicate> + using any_t = logic::any_t::value...>; + // The tables not covered by the from. using _required_tables = detail::make_difference_set_t< _all_required_tables, _all_provided_tables // Hint: extra_tables are not used here because they are just a helper for dynamic .add_*() >; + // The common table expressions not covered by the with. + using _required_ctes = detail::make_difference_set_t< + _all_required_ctes, + _all_provided_ctes + >; + using _result_type_provider = detail::get_last_if; struct _result_methods_t: public _result_type_provider::template _result_methods_t<_statement_t> @@ -108,10 +130,12 @@ namespace sqlpp // A select can be used as a pseudo table if // - at least one column is selected - // - the select is complete (leaks no tables) + // - the select is complete (leaks no table requirements) static constexpr bool _can_be_used_as_table() { - return is_select_column_list_t<_result_type_provider>::value and _required_tables::size::value == 0 + return has_result_row_t<_statement_t>::value + and _required_tables::size::value == 0 + and _all_provided_ctes::size::value == 0 // a sub-select must not contain a WITH ? true : false; } @@ -133,6 +157,8 @@ namespace sqlpp struct _recursive_traits { + using _required_ctes = statement_policies_t::_required_ctes; + using _provided_ctes = detail::type_set<>; using _required_tables = statement_policies_t::_required_tables; using _provided_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; @@ -143,6 +169,8 @@ namespace sqlpp detail::type_set<>>::type; }; + using _cte_check = typename std::conditional<_required_ctes::size::value == 0, + consistent_t, assert_no_unknown_ctes_t>::type; using _table_check = typename std::conditional<_required_tables::size::value == 0, consistent_t, assert_no_unknown_tables_t>::type; using _parameter_check = typename std::conditional::value == 0, @@ -158,13 +186,15 @@ namespace sqlpp { using _policies_t = typename detail::statement_policies_t; - using _run_check = detail::get_first_if::_consistency_check..., typename _policies_t::_table_check>; using _prepare_check = detail::get_first_if::_consistency_check..., - typename _policies_t::_table_check>; + typename _policies_t::_cte_check, + _consistency_check>; + using _run_check = detail::get_first_if; using _result_type_provider = typename _policies_t::_result_type_provider; template diff --git a/include/sqlpp11/table.h b/include/sqlpp11/table.h index b7bd5065..e5e6549c 100644 --- a/include/sqlpp11/table.h +++ b/include/sqlpp11/table.h @@ -48,11 +48,13 @@ namespace sqlpp struct _recursive_traits { - using _parameters = std::tuple<>; + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::type_set<>; using _required_tables = detail::type_set<>; using _provided_tables = detail::type_set
; using _provided_outer_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; using _tags = detail::type_set<>; }; diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index e7befef0..4985a875 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -44,11 +44,13 @@ namespace sqlpp struct _recursive_traits { - using _parameters = std::tuple<>; + using _required_ctes = required_ctes_of
; + using _provided_ctes = detail::type_set<>; using _required_tables = detail::type_set<>; using _provided_tables = detail::type_set; using _provided_outer_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; using _tags = detail::type_set<>; }; diff --git a/include/sqlpp11/table_ref.h b/include/sqlpp11/table_ref.h new file mode 100644 index 00000000..58211093 --- /dev/null +++ b/include/sqlpp11/table_ref.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_TABLE_REF_H +#define SQLPP_TABLE_REF_H + +#include + +namespace sqlpp +{ + template + auto from_table(T t) -> T + { + return t; + } + + template + struct from_table_impl + { + using type = T; + }; + template + using from_table_t = typename from_table_impl::type; + + template + auto table_ref(T t) -> T + { + return t; + } +} + +#endif diff --git a/include/sqlpp11/transaction.h b/include/sqlpp11/transaction.h index b0d456cc..c140d168 100644 --- a/include/sqlpp11/transaction.h +++ b/include/sqlpp11/transaction.h @@ -90,7 +90,7 @@ namespace sqlpp transaction_t start_transaction(Db& db, bool report_unfinished_transaction = report_auto_rollback) { return { db, report_unfinished_transaction }; - }; + } } #endif diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 04ffc42a..e6dc033f 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -39,7 +39,7 @@ namespace sqlpp { struct can_be_null{}; struct contains_aggregate_function{}; - }; + } namespace detail { @@ -74,7 +74,7 @@ namespace sqlpp namespace tag { struct is_expression{}; - }; + } namespace detail { template @@ -92,7 +92,7 @@ namespace sqlpp namespace tag\ {\ struct name{};\ - };\ + }\ namespace detail\ {\ template\ @@ -103,68 +103,71 @@ namespace sqlpp template\ using name##_t = typename detail::name##_impl::type; - SQLPP_VALUE_TRAIT_GENERATOR(is_value_type); - SQLPP_VALUE_TRAIT_GENERATOR(is_sql_null); - SQLPP_VALUE_TRAIT_GENERATOR(is_boolean); - SQLPP_VALUE_TRAIT_GENERATOR(is_integral); - SQLPP_VALUE_TRAIT_GENERATOR(is_floating_point); + SQLPP_VALUE_TRAIT_GENERATOR(is_value_type) + SQLPP_VALUE_TRAIT_GENERATOR(is_sql_null) + SQLPP_VALUE_TRAIT_GENERATOR(is_boolean) + SQLPP_VALUE_TRAIT_GENERATOR(is_integral) + SQLPP_VALUE_TRAIT_GENERATOR(is_floating_point) template using is_numeric_t = logic::any_t< detail::is_element_of::value, detail::is_element_of::value>; - SQLPP_VALUE_TRAIT_GENERATOR(is_text); - SQLPP_VALUE_TRAIT_GENERATOR(is_wrapped_value); - SQLPP_VALUE_TRAIT_GENERATOR(is_selectable); - SQLPP_VALUE_TRAIT_GENERATOR(is_multi_expression); - SQLPP_VALUE_TRAIT_GENERATOR(is_alias); - SQLPP_VALUE_TRAIT_GENERATOR(is_select_flag); - SQLPP_VALUE_TRAIT_GENERATOR(is_result_field); - SQLPP_VALUE_TRAIT_GENERATOR(must_not_insert); - SQLPP_VALUE_TRAIT_GENERATOR(must_not_update); - SQLPP_VALUE_TRAIT_GENERATOR(require_insert); - SQLPP_VALUE_TRAIT_GENERATOR(trivial_value_is_null); - SQLPP_VALUE_TRAIT_GENERATOR(null_is_trivial_value); + SQLPP_VALUE_TRAIT_GENERATOR(is_text) + SQLPP_VALUE_TRAIT_GENERATOR(is_wrapped_value) + SQLPP_VALUE_TRAIT_GENERATOR(is_selectable) + SQLPP_VALUE_TRAIT_GENERATOR(is_multi_expression) + SQLPP_VALUE_TRAIT_GENERATOR(is_alias) + SQLPP_VALUE_TRAIT_GENERATOR(is_select_flag) + SQLPP_VALUE_TRAIT_GENERATOR(is_result_field) - SQLPP_VALUE_TRAIT_GENERATOR(is_statement); - SQLPP_VALUE_TRAIT_GENERATOR(is_prepared_statement); - SQLPP_VALUE_TRAIT_GENERATOR(is_noop); - SQLPP_VALUE_TRAIT_GENERATOR(is_missing); - SQLPP_VALUE_TRAIT_GENERATOR(is_return_value); - SQLPP_VALUE_TRAIT_GENERATOR(is_table); - SQLPP_VALUE_TRAIT_GENERATOR(is_join); - SQLPP_VALUE_TRAIT_GENERATOR(is_pseudo_table); - SQLPP_VALUE_TRAIT_GENERATOR(is_column); - SQLPP_VALUE_TRAIT_GENERATOR(is_select); - SQLPP_VALUE_TRAIT_GENERATOR(is_select_flag_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_select_column_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_from); - SQLPP_VALUE_TRAIT_GENERATOR(is_single_table); - SQLPP_VALUE_TRAIT_GENERATOR(is_into); - SQLPP_VALUE_TRAIT_GENERATOR(is_extra_tables); - SQLPP_VALUE_TRAIT_GENERATOR(is_on); - SQLPP_VALUE_TRAIT_GENERATOR(is_where); - SQLPP_VALUE_TRAIT_GENERATOR(is_group_by); - SQLPP_VALUE_TRAIT_GENERATOR(is_having); - SQLPP_VALUE_TRAIT_GENERATOR(is_order_by); - SQLPP_VALUE_TRAIT_GENERATOR(is_limit); - SQLPP_VALUE_TRAIT_GENERATOR(is_offset); - SQLPP_VALUE_TRAIT_GENERATOR(is_union); - SQLPP_VALUE_TRAIT_GENERATOR(is_using_); - SQLPP_VALUE_TRAIT_GENERATOR(is_column_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_multi_column); - SQLPP_VALUE_TRAIT_GENERATOR(is_value_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_assignment); - SQLPP_VALUE_TRAIT_GENERATOR(is_update_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_insert_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_insert_value); - SQLPP_VALUE_TRAIT_GENERATOR(is_insert_value_list); - SQLPP_VALUE_TRAIT_GENERATOR(is_sort_order); - SQLPP_VALUE_TRAIT_GENERATOR(is_parameter); + SQLPP_VALUE_TRAIT_GENERATOR(must_not_insert) + SQLPP_VALUE_TRAIT_GENERATOR(must_not_update) + SQLPP_VALUE_TRAIT_GENERATOR(require_insert) + SQLPP_VALUE_TRAIT_GENERATOR(trivial_value_is_null) + SQLPP_VALUE_TRAIT_GENERATOR(null_is_trivial_value) - SQLPP_VALUE_TRAIT_GENERATOR(requires_braces); + SQLPP_VALUE_TRAIT_GENERATOR(is_statement) + SQLPP_VALUE_TRAIT_GENERATOR(is_prepared_statement) + SQLPP_VALUE_TRAIT_GENERATOR(is_union) + SQLPP_VALUE_TRAIT_GENERATOR(is_with) + SQLPP_VALUE_TRAIT_GENERATOR(is_cte) + SQLPP_VALUE_TRAIT_GENERATOR(is_noop) + SQLPP_VALUE_TRAIT_GENERATOR(is_missing) + SQLPP_VALUE_TRAIT_GENERATOR(is_return_value) + SQLPP_VALUE_TRAIT_GENERATOR(is_table) + SQLPP_VALUE_TRAIT_GENERATOR(is_join) + SQLPP_VALUE_TRAIT_GENERATOR(is_pseudo_table) + SQLPP_VALUE_TRAIT_GENERATOR(is_column) + SQLPP_VALUE_TRAIT_GENERATOR(is_select) + SQLPP_VALUE_TRAIT_GENERATOR(is_select_flag_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_select_column_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_from) + SQLPP_VALUE_TRAIT_GENERATOR(is_single_table) + SQLPP_VALUE_TRAIT_GENERATOR(is_into) + SQLPP_VALUE_TRAIT_GENERATOR(is_extra_tables) + SQLPP_VALUE_TRAIT_GENERATOR(is_on) + SQLPP_VALUE_TRAIT_GENERATOR(is_where) + SQLPP_VALUE_TRAIT_GENERATOR(is_group_by) + SQLPP_VALUE_TRAIT_GENERATOR(is_having) + SQLPP_VALUE_TRAIT_GENERATOR(is_order_by) + SQLPP_VALUE_TRAIT_GENERATOR(is_limit) + SQLPP_VALUE_TRAIT_GENERATOR(is_offset) + SQLPP_VALUE_TRAIT_GENERATOR(is_using_) + SQLPP_VALUE_TRAIT_GENERATOR(is_column_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_multi_column) + SQLPP_VALUE_TRAIT_GENERATOR(is_value_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_assignment) + SQLPP_VALUE_TRAIT_GENERATOR(is_update_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_insert_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_insert_value) + SQLPP_VALUE_TRAIT_GENERATOR(is_insert_value_list) + SQLPP_VALUE_TRAIT_GENERATOR(is_sort_order) + SQLPP_VALUE_TRAIT_GENERATOR(is_parameter) - SQLPP_VALUE_TRAIT_GENERATOR(enforce_null_result_treatment); + SQLPP_VALUE_TRAIT_GENERATOR(requires_braces) + + SQLPP_VALUE_TRAIT_GENERATOR(enforce_null_result_treatment) template using tag_if = typename std::conditional::type; @@ -183,6 +186,12 @@ namespace sqlpp template using cpp_value_type_of = typename value_type_of::_cpp_value_type; + template + using required_ctes_of = typename T::_recursive_traits::_required_ctes; + + template + using provided_ctes_of = typename T::_recursive_traits::_provided_ctes; + template using required_tables_of = typename T::_recursive_traits::_required_tables; @@ -217,6 +226,8 @@ namespace sqlpp template struct make_recursive_traits { + using _required_ctes = detail::make_joined_set_t...>; + using _provided_ctes = detail::make_joined_set_t...>; using _required_tables = detail::make_joined_set_t...>; using _provided_tables = detail::make_joined_set_t...>; using _provided_outer_tables = detail::make_joined_set_t...>; @@ -228,6 +239,8 @@ namespace sqlpp template struct recursive_tags { + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::type_set<>; using _required_tables = detail::type_set<>; using _provided_tables = detail::type_set<>; using _provided_outer_tables = detail::type_set<>; @@ -269,7 +282,7 @@ namespace sqlpp static void _() { static_assert(wrong_t::value, "connection cannot run something that is neither statement nor prepared statement"); - }; + } }; struct assert_prepare_statement_t @@ -280,9 +293,24 @@ namespace sqlpp static void _() { static_assert(wrong_t::value, "connection cannot prepare something that is not a statement"); - }; + } }; + template + struct consistency_check + { + using type = assert_run_statement_or_prepared_t; + }; + + template + struct consistency_check::value or is_prepared_statement_t::value>::type> + { + using type = typename T::_consistency_check; + }; + + template + using consistency_check_t = typename consistency_check::type; + template struct run_check { @@ -329,6 +357,57 @@ namespace sqlpp template using serialize_check_t = typename serialize_check::type; + template + struct has_result_row_impl + { + using type = std::false_type; + }; + + template + struct has_result_row_impl::template _result_row_t>::value, + void>::type> + { + using type = std::true_type; + }; + + template + using has_result_row_t = typename has_result_row_impl::type; + + template + struct get_result_row_impl + { + using type = void; + }; + + template + struct get_result_row_impl::template _result_row_t>::value, + void>::type> + { + using type = typename Statement::template _result_methods_t::template _result_row_t; + }; + + template + using get_result_row_t = typename get_result_row_impl::type; + + template class Predicate, typename Enable=void> + struct has_policy_impl + { + using type = std::false_type; + }; + + template class Predicate> + struct has_policy_impl::value>::type> + { + using type = std::true_type; + }; + + template class Predicate> + using has_policy_t = typename has_policy_impl::type; + } #endif diff --git a/include/sqlpp11/union.h b/include/sqlpp11/union.h index 615321dc..066e73e3 100644 --- a/include/sqlpp11/union.h +++ b/include/sqlpp11/union.h @@ -27,53 +27,19 @@ #ifndef SQLPP_UNION_H #define SQLPP_UNION_H +#include +#include #include #include #include #include #include #include +#include #include namespace sqlpp { - template - struct has_result_row_impl - { - using type = std::false_type; - }; - - template - struct has_result_row_impl::template _result_row_t>::value, - void>::type> - { - using type = std::true_type; - }; - - template - using has_result_row_t = typename has_result_row_impl::type; - - template - struct get_result_row_impl - { - using type = void; - }; - - template - struct get_result_row_impl::template _result_row_t>::value, - void>::type> - { - using type = typename Statement::template _result_methods_t::template _result_row_t; - }; - - template - using get_result_row_t = typename get_result_row_impl::type; - - struct no_union_t; using blank_union_t = statement_t struct union_statement_impl { - using type = statement_t; + using type = statement_t; }; template @@ -95,31 +61,11 @@ namespace sqlpp template using union_statement_t = typename union_statement_impl::type; - - // UNION DATA - template - struct union_data_t - { - union_data_t(Lhs lhs, Rhs rhs): - _lhs(lhs), - _rhs(rhs) - {} - - union_data_t(const union_data_t&) = default; - union_data_t(union_data_t&&) = default; - union_data_t& operator=(const union_data_t&) = default; - union_data_t& operator=(union_data_t&&) = default; - ~union_data_t() = default; - - Lhs _lhs; - Rhs _rhs; - }; - // UNION(EXPR) template struct union_t { - using _traits = make_traits; + using _traits = make_traits; using _recursive_traits = make_recursive_traits; using _alias_t = struct{}; @@ -145,9 +91,9 @@ namespace sqlpp _impl_t& operator()() { return union_; } const _impl_t& operator()() const { return union_; } - using _selected_columns_t = decltype(union_._data._lhs.selected_columns); - _selected_columns_t& get_selected_columns() { return union_._data._lhs.selected_columns;} - const _selected_columns_t& get_selected_columns() const { return union_._data._lhs.selected_columns; } + using _selected_columns_t = typename std::decay::type; + _selected_columns_t& get_selected_columns() { return union_._data._lhs.get_selected_columns(); } + const _selected_columns_t& get_selected_columns() const { return union_._data._lhs.get_selected_columns(); } template static auto _get_member(T t) -> decltype(t.union_) @@ -155,7 +101,9 @@ namespace sqlpp return t.union_; } - using _consistency_check = consistent_t; + using _consistency_check = detail::get_first_if; }; template @@ -197,7 +145,7 @@ namespace sqlpp using _database_t = typename Policies::_database_t; template - using _check = logic::all_t::value...>; // FIXME and consistent/runnable + using _check = logic::all_t::value...>; template using _new_statement_t = union_statement_t; @@ -209,9 +157,13 @@ namespace sqlpp -> _new_statement_t<_check, union_t, Rhs>> { static_assert(is_statement_t::value, "argument of union call has to be a statement"); - static_assert(has_result_row_t::value, "argument of a union has to be a (complete) select statement"); - static_assert(has_result_row_t>::value, "left hand side argument of a union has to be a (complete) select statement"); - static_assert(std::is_same>, get_result_row_t>::value, "both select statements in a union have to have the same result columns (type and name)"); + static_assert(has_policy_t::value, "argument of union call has to be a select"); + static_assert(has_result_row_t::value, "argument of a union has to be a complete select statement"); + static_assert(has_result_row_t>::value, "left hand side argument of a union has to be a complete select statement or union"); + + using _result_row_t = get_result_row_t; + static_assert(std::is_same>, _result_row_t>::value, "both arguments in a union have to have the same result columns (type and name)"); + static_assert(is_static_result_row_t<_result_row_t>::value, "unions must not have dynamically added columns"); return _union_impl(_check, Rhs>{}, rhs); } @@ -221,10 +173,13 @@ namespace sqlpp -> _new_statement_t<_check, union_t, Rhs>> { static_assert(is_statement_t::value, "argument of union call has to be a statement"); + static_assert(has_policy_t::value, "argument of union call has to be a select"); static_assert(has_result_row_t::value, "argument of a union has to be a (complete) select statement"); static_assert(has_result_row_t>::value, "left hand side argument of a union has to be a (complete) select statement"); - static_assert(std::is_same>, get_result_row_t>::value, "both select statements in a union have to have the same result columns (type and name)"); + using _result_row_t = get_result_row_t; + static_assert(std::is_same>, _result_row_t>::value, "both arguments in a union have to have the same result columns (type and name)"); + static_assert(is_static_result_row_t<_result_row_t>::value, "unions must not have dynamically added columns"); return _union_impl(_check, Rhs>{}, rhs); } @@ -245,26 +200,6 @@ namespace sqlpp }; }; - // Interpreters - template - struct serializer_t> - { - using _serialize_check = serialize_check_of; - using T = union_data_t; - - static Context& _(const T& t, Context& context) - { - context << '('; - serialize(t._lhs, context); - context << ") UNION "; - serialize(Flag{}, context); - context << " ("; - serialize(t._rhs, context); - context << ')'; - return context; - } - }; - } #endif diff --git a/include/sqlpp11/union_data.h b/include/sqlpp11/union_data.h new file mode 100644 index 00000000..fd9bba32 --- /dev/null +++ b/include/sqlpp11/union_data.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_UNION_DATA_H +#define SQLPP_UNION_DATA_H + +#include + +namespace sqlpp +{ + template + struct union_data_t + { + union_data_t(Lhs lhs, Rhs rhs): + _lhs(lhs), + _rhs(rhs) + {} + + union_data_t(const union_data_t&) = default; + union_data_t(union_data_t&&) = default; + union_data_t& operator=(const union_data_t&) = default; + union_data_t& operator=(union_data_t&&) = default; + ~union_data_t() = default; + + Lhs _lhs; + Rhs _rhs; + }; + + // Interpreters + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = union_data_t; + + static Context& _(const T& t, Context& context) + { + context << '('; + serialize(t._lhs, context); + context << ") UNION "; + serialize(Flag{}, context); + context << " ("; + serialize(t._rhs, context); + context << ')'; + return context; + } + }; + +} + +#endif diff --git a/include/sqlpp11/update_list.h b/include/sqlpp11/update_list.h index ba5ddc8d..5b0d5bf2 100644 --- a/include/sqlpp11/update_list.h +++ b/include/sqlpp11/update_list.h @@ -147,7 +147,7 @@ namespace sqlpp static void _() { static_assert(wrong_t::value, "update assignments required, i.e. set(...)"); - }; + } }; struct no_update_list_t diff --git a/include/sqlpp11/using.h b/include/sqlpp11/using.h index cf1bdea8..f1d9537d 100644 --- a/include/sqlpp11/using.h +++ b/include/sqlpp11/using.h @@ -192,7 +192,7 @@ namespace sqlpp static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in using()"); return { static_cast&>(*this), using_data_t{args...} }; - }; + } }; }; diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index 161da725..81db3c95 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -190,7 +190,7 @@ namespace sqlpp static void _() { static_assert(wrong_t::value, "where expression required, e.g. where(true)"); - }; + } }; // NO WHERE YET diff --git a/include/sqlpp11/with.h b/include/sqlpp11/with.h new file mode 100644 index 00000000..71f8e3a1 --- /dev/null +++ b/include/sqlpp11/with.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_WITH_H +#define SQLPP_WITH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +namespace sqlpp +{ + template + struct with_data_t + { + using _is_recursive = logic::any_t; + + with_data_t(Expressions... expressions): + _expressions(expressions...) + {} + + with_data_t(const with_data_t&) = default; + with_data_t(with_data_t&&) = default; + with_data_t& operator=(const with_data_t&) = default; + with_data_t& operator=(with_data_t&&) = default; + ~with_data_t() = default; + + std::tuple _expressions; + }; + + template + struct with_t + { + using _traits = make_traits; + struct _recursive_traits + { + using _required_ctes = detail::type_set<>; + using _provided_ctes = detail::make_joined_set_t...>; // with provides common table expressions + using _required_tables = detail::type_set<>; + using _provided_tables = detail::type_set<>; + using _provided_outer_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _parameters = detail::make_parameter_tuple_t...>; + using _tags = detail::type_set<>; + }; + + using _is_dynamic = is_database; + + // Data + using _data_t = with_data_t; + + // Member implementation with data and methods + template + struct _impl_t + { + public: + _data_t _data; + }; + + // Base template to be inherited by the statement + template + struct _base_t + { + using _data_t = with_data_t; + + _impl_t with; + _impl_t& operator()() { return with; } + const _impl_t& operator()() const { return with; } + + template + static auto _get_member(T t) -> decltype(t.with) + { + return t.with; + } + + // FIXME: Need real checks here + using _consistency_check = consistent_t; + }; + }; + + + struct no_with_t + { + using _traits = make_traits; + using _recursive_traits = make_recursive_traits<>; + + // Data + using _data_t = no_data_t; + + // Member implementation with data and methods + template + struct _impl_t + { + _data_t _data; + }; + + // Base template to be inherited by the statement + template + struct _base_t + { + using _data_t = no_data_t; + + _impl_t no_with; + _impl_t& operator()() { return no_with; } + const _impl_t& operator()() const { return no_with; } + + template + static auto _get_member(T t) -> decltype(t.no_with) + { + return t.no_with; + } + + using _consistency_check = consistent_t; + + }; + }; + + template + struct blank_with_t + { + with_data_t _data; + + template + auto operator()(Statement statement) + -> new_statement_t> + { + // FIXME need checks here + // check that no cte refers to any of the ctes to the right + return { statement, _data }; + } + }; + + // Interpreters + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = with_data_t; + + static Context& _(const T& t, Context& context) + { + // FIXME: If there is a recursive CTE, add a "RECURSIVE" here + context << " WITH "; + if (T::_is_recursive::value) + context << "RECURSIVE "; + interpret_tuple(t._expressions, ',', context); + context << ' '; + return context; + } + }; + + template + auto with(Expressions... cte) + -> blank_with_t + { + static_assert(logic::all_t::value...>::value, "at least one expression in with is not a common table expression"); + static_assert(logic::none_t::value...>::value, "at least one expression in with is an incomplete common table expression"); + return { {cte...} }; + } +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f3127acf..74505083 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,7 @@ endmacro () #build_and_run(Minimalistic) #build_and_run(ResultTest) build_and_run(UnionTest) +build_and_run(WithTest) # if you want to use the generator, you can do something like this: #find_package(PythonInterp REQUIRED) diff --git a/tests/FunctionTest.cpp b/tests/FunctionTest.cpp index b0026480..bde10db5 100644 --- a/tests/FunctionTest.cpp +++ b/tests/FunctionTest.cpp @@ -33,7 +33,7 @@ #include MockDb db = {}; -SQLPP_ALIAS_PROVIDER(kaesekuchen); +SQLPP_ALIAS_PROVIDER(kaesekuchen) int main() { diff --git a/tests/MockDb.h b/tests/MockDb.h index 567866d2..9ec072a1 100644 --- a/tests/MockDb.h +++ b/tests/MockDb.h @@ -103,7 +103,7 @@ struct MockDbT: public sqlpp::connection void next(ResultRow& result_row) { result_row._invalidate(); - }; + } }; // Directly executed statements start here diff --git a/tests/SelectTypeTest.cpp b/tests/SelectTypeTest.cpp index bd0e9be8..70f61811 100644 --- a/tests/SelectTypeTest.cpp +++ b/tests/SelectTypeTest.cpp @@ -38,10 +38,10 @@ MockDb::_serializer_context_t printer; namespace alias { - SQLPP_ALIAS_PROVIDER(a); - SQLPP_ALIAS_PROVIDER(b); - SQLPP_ALIAS_PROVIDER(left); - SQLPP_ALIAS_PROVIDER(right); + SQLPP_ALIAS_PROVIDER(a) + SQLPP_ALIAS_PROVIDER(b) + SQLPP_ALIAS_PROVIDER(left) + SQLPP_ALIAS_PROVIDER(right) } int main() diff --git a/tests/UnionTest.cpp b/tests/UnionTest.cpp index b4462ded..ca88841c 100644 --- a/tests/UnionTest.cpp +++ b/tests/UnionTest.cpp @@ -37,12 +37,17 @@ int main() test::TabBar t; test::TabFoo f; - db(select(t.alpha).from(t).union_distinct(select(f.epsilon.as(t.alpha)).from(f))); - db(select(t.alpha).from(t).union_all(select(f.epsilon.as(t.alpha)).from(f))); + db(select(t.alpha).from(t).where(true) + .union_distinct(select(f.epsilon.as(t.alpha)).from(f).where(true))); + db(select(t.alpha).from(t).where(true) + .union_all(select(f.epsilon.as(t.alpha)).from(f).where(true))); - auto u = select(t.alpha).from(t).union_all(select(f.epsilon.as(t.alpha)).from(f)).as(sqlpp::alias::u); + auto u = select(t.alpha).from(t).where(true).union_all(select(f.epsilon.as(t.alpha)).from(f).where(true)).as(sqlpp::alias::u); - db(select(all_of(u)).from(u).union_all(select(t.delta.as(t.alpha)).from(t))); + db(select(all_of(u)).from(u).where(true).union_all(select(t.delta.as(t.alpha)).from(t).where(true))); + db(select(u.alpha).from(u).where(true).union_all(select(t.delta.as(t.alpha)).from(t).where(true))); + + db(select(t.alpha).from(t).where(true).union_all(select(t.alpha).from(t).where(true)).union_all(select(t.alpha).from(t).where(true))); return 0; } diff --git a/tests/WithTest.cpp b/tests/WithTest.cpp new file mode 100644 index 00000000..1ee5cd14 --- /dev/null +++ b/tests/WithTest.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Sample.h" +#include "MockDb.h" +#include +#include +#include + +MockDb db; +MockDb::_serializer_context_t printer; + +int main() +{ + const auto t = test::TabBar{}; + + auto x = cte(sqlpp::x).as(select(all_of(t)).from(t)); + + db(with(x)(select(x.alpha).from(x).where(true))); + + auto y0 = cte(sqlpp::y).as(select(all_of(t)).from(t)); + auto y = y0.union_all(select(all_of(y0)).from(y0).where(false)); + + std::cout << serialize(y, printer).str() << std::endl; printer.reset(); + std::cout << serialize(from_table(y), printer).str() << std::endl; + + db(with(y)(select(y.alpha).from(y).where(true))); + + return 0; +}