From 902583a33b02c9a48985f1833ae080e4347fe4fc Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Wed, 9 Oct 2013 10:36:38 +0200 Subject: [PATCH] Added support for dynamic columns to select_expression_list Dynamic columns are not yet supported in results, though. --- include/sqlpp11/detail/named_serializable.h | 98 +++++++++++++++++++++ include/sqlpp11/select.h | 65 +++++++++++--- include/sqlpp11/select_expression_list.h | 75 +++++++++++++--- include/sqlpp11/select_fwd.h | 2 +- tests/SelectTest.cpp | 9 +- 5 files changed, 221 insertions(+), 28 deletions(-) create mode 100644 include/sqlpp11/detail/named_serializable.h diff --git a/include/sqlpp11/detail/named_serializable.h b/include/sqlpp11/detail/named_serializable.h new file mode 100644 index 00000000..45f0d383 --- /dev/null +++ b/include/sqlpp11/detail/named_serializable.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013, 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_NAMED_SERIALIZABLE_H +#define SQLPP_NAMED_SERIALIZABLE_H + +#include +#include +#include + +namespace sqlpp +{ + namespace detail + { + template + struct named_serializable_t + { + template + named_serializable_t(T&& t): + _impl(std::make_shared<_impl_t::type>>(std::forward(t))) + {} + + named_serializable_t(const named_serializable_t&) = default; + named_serializable_t(named_serializable_t&&) = default; + named_serializable_t& operator=(const named_serializable_t&) = default; + named_serializable_t& operator=(named_serializable_t&&) = default; + ~named_serializable_t() = default; + + void serialize(std::ostream& os, Db& db) const + { + _impl->serialize(os, db); + } + + std::string _get_name() const + { + _impl->_get_name(); + } + + private: + struct _impl_base + { + virtual void serialize(std::ostream& os, Db& db) const = 0; + virtual std::string _get_name() const = 0; + }; + + template + struct _impl_t: public _impl_base + { + _impl_t(const T& t): + _t(t) + {} + + _impl_t(T&& t): + _t(std::move(t)) + {} + + void serialize(std::ostream& os, Db& db) const + { + _t.serialize(os, db); + } + + std::string _get_name() const + { + T::_name_t::_get_name(); + } + + T _t; + }; + + std::shared_ptr _impl; + }; + } +} + +#endif diff --git a/include/sqlpp11/select.h b/include/sqlpp11/select.h index 53d09950..62d4fa91 100644 --- a/include/sqlpp11/select.h +++ b/include/sqlpp11/select.h @@ -27,13 +27,11 @@ #ifndef SQLPP_SELECT_H #define SQLPP_SELECT_H -#include #include #include #include #include #include -#include #include #include #include @@ -42,7 +40,6 @@ #include #include #include -#include #include #include @@ -55,7 +52,7 @@ namespace sqlpp template< typename Database, typename Flags, - typename... NamedExpr, + typename ExpressionList, typename From, typename Where, typename GroupBy, @@ -64,11 +61,11 @@ namespace sqlpp typename Limit, typename Offset > - struct select_t>, From, Where, GroupBy, Having, OrderBy, Limit, Offset> - : public select_expression_list_t>::_value_type::template operators>, + ExpressionList, From, Where, GroupBy, @@ -79,7 +76,6 @@ namespace sqlpp { using _Database = Database; using _From = From; - using ExpressionList = select_expression_list_t>; static_assert(is_noop::value or is_select_flag_list_t::value, "invalid list of select flags"); static_assert(is_select_expression_list_t::value, "invalid list of select expressions"); @@ -94,6 +90,8 @@ namespace sqlpp using _is_select = std::true_type; using _requires_braces = std::true_type; + template + using set_expression_list_t = select_t; template using set_from_t = select_t; template @@ -109,7 +107,7 @@ namespace sqlpp template using set_offset_t = select_t; - using _result_row_t = result_row_t...>; + using _result_row_t = typename ExpressionList::_result_row_t; // Indicators using _value_type = typename std::conditional< @@ -166,6 +164,34 @@ namespace sqlpp { } + auto dynamic_columns() + -> set_expression_list_t> + { + static_assert(not std::is_same::value, "cannot call dynamic_from() in a non-dynamic select"); + static_assert(is_noop::value, "cannot call dynamic_columns() after from()"); + return { + _flags, + {_expression_list._expressions}, + _from, + _where, + _group_by, + _having, + _order_by, + _limit, + _offset + }; + } + + template + select_t& add_column(NamedExpr&& namedExpr) + { + static_assert(is_dynamic_t::value, "cannot call add_column() in a non-dynamic column list"); + + _expression_list.add(std::forward(namedExpr)); + + return *this; + } + // sqlpp functions template auto from(Table&&... table) @@ -497,7 +523,7 @@ namespace sqlpp template struct _pseudo_table_t { - using table = select_pseudo_table_t; + using table = typename ExpressionList::template _pseudo_table_t; using alias = typename table::template alias_t; }; @@ -573,11 +599,12 @@ namespace sqlpp { template using make_select_expression_list_t = - select_expression_list_t()...))>; + select_expression_list_t()...))>; } template - select_t, detail::make_select_expression_list_t> select(NamedExpr&&... namedExpr) + auto select(NamedExpr&&... namedExpr) + -> select_t, detail::make_select_expression_list_t> { return { { detail::make_flag_tuple(std::forward(namedExpr)...) }, @@ -585,12 +612,24 @@ namespace sqlpp }; } template - select_t::type, detail::make_select_flag_list_t, detail::make_select_expression_list_t> dynamic_select(const Db& db, NamedExpr&&... namedExpr) + auto dynamic_select(const Db& db, NamedExpr&&... namedExpr) + -> select_t::type, detail::make_select_flag_list_t, detail::make_select_expression_list_t> { return { { detail::make_flag_tuple(std::forward(namedExpr)...) }, { detail::make_expression_tuple(std::forward(namedExpr)...) } }; } + +#warning: need to add dynamic fields + /* Idea: Use a vector of serializable or similar for select. + * Translate the vector into a map, first = name, second = something similar to text which knows its index + * + * What about default constructing the result? Not a problem. The map is empty then. + * + * But how to transport the vector of serializables from the select into the result? + * + * Maybe store the names of the map content in a field in the query? + */ } #endif diff --git a/include/sqlpp11/select_expression_list.h b/include/sqlpp11/select_expression_list.h index 61cdb624..1c84628b 100644 --- a/include/sqlpp11/select_expression_list.h +++ b/include/sqlpp11/select_expression_list.h @@ -32,7 +32,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include @@ -40,17 +44,39 @@ namespace sqlpp { namespace detail { - template - struct get_first_argument + template + struct get_first_argument_if_unique { - using type = T; + using _value_type = no_value_t; + struct _name_t {}; }; + + template + struct get_first_argument_if_unique + { + using _value_type = typename T::_value_type; + using _name_t = typename T::_name_t; + }; + + template + struct dynamic_select_expression_list + { + using type = std::vector>; + }; + + template<> + struct dynamic_select_expression_list + { + using type = std::vector; + }; + } - template - struct select_expression_list_t> + + template + struct select_expression_list_t> { - // check for at least one select expression - static_assert(sizeof...(NamedExpr), "at least one select expression required"); + using _is_select_expression_list = std::true_type; + using _is_dynamic = typename std::conditional::value, std::false_type, std::true_type>::type; // check for duplicate select expressions static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected"); @@ -64,27 +90,50 @@ namespace sqlpp // check for duplicate select expression names static_assert(not detail::has_duplicates::value, "at least one duplicate name detected"); - // declare this to be a select expression - using _is_select_expression_list = std::true_type; - // provide type information for sub-selects that are used as expressions struct _column_type {}; - struct _value_type: std::conditional::type::_value_type, no_value_t>::type + struct _value_type: detail::get_first_argument_if_unique::_value_type { using _is_expression = typename std::conditional::type; using _is_named_expression = typename std::conditional::type; using _is_alias = std::false_type; }; - struct _no_name_t {}; - using _name_t = typename std::conditional::type::_name_t, _no_name_t>::type; + using _name_t = typename detail::get_first_argument_if_unique::_name_t; + + using _result_row_t = result_row_t...>; + + template + using _pseudo_table_t = select_pseudo_table_t; + + template + using _dynamic_t = select_expression_list_t>; + + template + void add(Expr&& namedExpr) + { + static_assert(is_named_expression_t::type>::value, "select() arguments require to be named expressions"); + _dynamic_expressions.push_back(std::forward(namedExpr)); + } template void serialize(std::ostream& os, Db& db) const { + // check for at least one select expression + static_assert(_is_dynamic::value or sizeof...(NamedExpr), "at least one select expression required"); + detail::serialize_tuple(os, db, _expressions, ','); + bool first = sizeof...(NamedExpr) == 0; + for (const auto column : _dynamic_expressions) + { + if (not first) + os << ','; + column.serialize(os, db); + first = false; + } } std::tuple _expressions; + typename detail::dynamic_select_expression_list::type _dynamic_expressions; }; } diff --git a/include/sqlpp11/select_fwd.h b/include/sqlpp11/select_fwd.h index e77143fd..48efba87 100644 --- a/include/sqlpp11/select_fwd.h +++ b/include/sqlpp11/select_fwd.h @@ -38,7 +38,7 @@ namespace sqlpp struct straight_join_t; template struct select_flag_list_t; - template struct select_expression_list_t; + template struct select_expression_list_t; template struct from_t; diff --git a/tests/SelectTest.cpp b/tests/SelectTest.cpp index f5ab0280..df7b9cab 100644 --- a/tests/SelectTest.cpp +++ b/tests/SelectTest.cpp @@ -25,9 +25,10 @@ #include "TabSample.h" #include +#include #include -class DbMock +class DbMock: public sqlpp::connection { public: const std::string& escape(const std::string& text) { return text; } @@ -273,6 +274,12 @@ int main() std::cerr << "------------------------\n"; } + // Test that select can be called with zero columns if it is used with dynamic columns. + { + auto s = dynamic_select(db).dynamic_columns().add_column(t.alpha); + s.serialize(std::cerr, db); + } + static_assert(sqlpp::is_select_flag_t::value, "sqlpp::all has to be a select_flag"); using T = sqlpp::detail::wrap_operand::type;