From 2e7d5478f6627f5616de0efd876d3480a1cf29a6 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Mon, 30 Sep 2013 07:46:50 +0200 Subject: [PATCH] Added dynamic functions to insert, remove and update --- include/sqlpp11/assignment_list.h | 53 ++++++++++++++--- include/sqlpp11/from.h | 2 +- include/sqlpp11/insert.h | 44 +++++++++++--- include/sqlpp11/insert_list.h | 95 +++++++++++++++++++++++++------ include/sqlpp11/remove.h | 63 +++++++++++++++++--- include/sqlpp11/update.h | 64 ++++++++++++++++++--- include/sqlpp11/using.h | 47 ++++++++++++++- tests/InsertTest.cpp | 3 + tests/RemoveTest.cpp | 4 ++ tests/UpdateTest.cpp | 4 ++ 10 files changed, 326 insertions(+), 53 deletions(-) diff --git a/include/sqlpp11/assignment_list.h b/include/sqlpp11/assignment_list.h index 6db35f3f..756a2436 100644 --- a/include/sqlpp11/assignment_list.h +++ b/include/sqlpp11/assignment_list.h @@ -27,39 +27,74 @@ #ifndef SQLPP_ASSIGNMENT_LIST_H #define SQLPP_ASSIGNMENT_LIST_H +#include +#include #include #include -#include +#include namespace sqlpp { - template class ProhibitPredicate, typename... Assignment> + namespace detail + { + template + struct dynamic_assignment_list + { + using type = std::vector>; + }; + + template<> + struct dynamic_assignment_list + { + using type = std::vector; + }; + }; + + template class ProhibitPredicate, typename... Assignments> struct assignment_list_t { + using _is_assignment_list = std::true_type; + using _is_dynamic = typename std::conditional::value, std::false_type, std::true_type>::type; + // check for at least one order expression - static_assert(sizeof...(Assignment), "at least one select expression required in set()"); + static_assert(_is_dynamic::value or sizeof...(Assignments), "at least one assignment expression required in set()"); // check for duplicate assignments - static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in set()"); + static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in set()"); // check for invalid assignments - using _assignment_set = typename detail::make_set_if::type; - static_assert(_assignment_set::size::value == sizeof...(Assignment), "at least one argument is not an assignment in set()"); + using _assignment_set = typename detail::make_set_if::type; + static_assert(_assignment_set::size::value == sizeof...(Assignments), "at least one argument is not an assignment in set()"); // check for prohibited assignments - using _prohibited_assignment_set = typename detail::make_set_if::type; + using _prohibited_assignment_set = typename detail::make_set_if::type; static_assert(_prohibited_assignment_set::size::value == 0, "at least one assignment is prohibited by its column definition in set()"); - using _is_assignment_list = std::true_type; + template + void add(Assignment&& assignment) + { + static_assert(is_assignment_t::type>::value, "set() arguments require to be assigments"); + static_assert(not ProhibitPredicate::type::column_type>::value, "set() argument must not be updated"); + _dynamic_assignments.push_back(std::forward(assignment)); + } template void serialize(std::ostream& os, Db& db) const { os << " SET "; detail::serialize_tuple(os, db, _assignments, ','); + bool first = sizeof...(Assignments) == 0; + for (const auto& assignment : _dynamic_assignments) + { + if (not first) + os << ','; + assignment.serialize(os, db); + first = false; + } } - std::tuple _assignments; + std::tuple _assignments; + typename detail::dynamic_assignment_list::type _dynamic_assignments; }; } diff --git a/include/sqlpp11/from.h b/include/sqlpp11/from.h index 568037fe..f6a381e5 100644 --- a/include/sqlpp11/from.h +++ b/include/sqlpp11/from.h @@ -42,7 +42,7 @@ namespace sqlpp using _is_from = std::true_type; // ensure one argument at least - static_assert(sizeof...(TableOrJoin), "at least one table or join argument required in from"); + static_assert(sizeof...(TableOrJoin), "at least one table or join argument required in from()"); // check for duplicate arguments static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in from()"); diff --git a/include/sqlpp11/insert.h b/include/sqlpp11/insert.h index daf28b30..53dae044 100644 --- a/include/sqlpp11/insert.h +++ b/include/sqlpp11/insert.h @@ -37,12 +37,14 @@ namespace sqlpp { template< + typename Database = void, typename Table = noop, typename InsertList = noop > struct insert_t; template< + typename Database, typename Table, typename InsertList > @@ -51,19 +53,40 @@ namespace sqlpp static_assert(is_noop::value or is_table_t
::value, "invalid 'Table' argument"); static_assert(is_noop::value or is_insert_list_t::value, "invalid 'InsertList' argument"); - template - using set_insert_list_t = insert_t::type...>>; + template + using set_insert_list_t = insert_t; template - set_insert_list_t set(Assignment&&... assignment) + auto set(Assignment&&... assignment) + -> set_insert_list_t::type...>> + { + static_assert(std::is_same::value, "cannot call set() twice"); + // FIXME: Need to check if all required columns are set + return { + _table, + insert_list_t::type...>{std::forward(assignment)...}, + }; + } + + template + auto dynamic_set(Assignment&&... assignment) + -> set_insert_list_t::type...>> { static_assert(std::is_same::value, "cannot call set() twice"); return { _table, - insert_list_t{std::forward(assignment)...}, + insert_list_t::type...>{std::forward(assignment)...}, }; } + template + void add_set(Assignment&& assignment) + { + static_assert(is_dynamic_t::value, "cannot call add_set() in a non-dynamic set"); + + _insert_list.add(std::forward(assignment)); + } + template const insert_t& serialize(std::ostream& os, Db& db) const { @@ -71,10 +94,7 @@ namespace sqlpp _table.serialize(os, db); if (is_noop::value) { - if (connector_has_empty_list_insert_t::type>::value) - os << " () VALUES()"; - else - os << " DEFAULT VALUES"; + detail::serialize_empty_insert_list(os, db); } else _insert_list.serialize(os, db); @@ -104,7 +124,13 @@ namespace sqlpp }; template - constexpr insert_t::type> insert_into(Table&& table) + insert_t::type> insert_into(Table&& table) + { + return {std::forward
(table)}; + } + + template + insert_t::type, typename std::decay
::type> dynamic_insert_into(Database&& db, Table&& table) { return {std::forward
(table)}; } diff --git a/include/sqlpp11/insert_list.h b/include/sqlpp11/insert_list.h index cc8c8c44..7c18e808 100644 --- a/include/sqlpp11/insert_list.h +++ b/include/sqlpp11/insert_list.h @@ -27,14 +27,38 @@ #ifndef SQLPP_INSERT_LIST_H #define SQLPP_INSERT_LIST_H +#include +#include #include #include -#include +#include namespace sqlpp { namespace detail { + template + void serialize_empty_insert_list(std::ostream& os, const Db& db) + { + + if (connector_has_empty_list_insert_t::type>::value) + os << " () VALUES()"; + else + os << " DEFAULT VALUES"; + } + + template + struct dynamic_column_list + { + using type = std::vector>; + }; + + template<> + struct dynamic_column_list + { + using type = std::vector; + }; + template struct insert_column { @@ -47,26 +71,27 @@ namespace sqlpp Column _column; }; } - template class ProhibitPredicate, typename... Assignment> + template class ProhibitPredicate, typename... Assignments> struct insert_list_t { + using _is_insert_list = std::true_type; + using _is_dynamic = typename std::conditional::value, std::false_type, std::true_type>::type; + // check for at least one order expression - static_assert(sizeof...(Assignment), "at least one select expression required in set()"); + static_assert(_is_dynamic::value or sizeof...(Assignments), "at least one select expression required in set()"); // check for duplicate assignments - static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in set()"); + static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in set()"); // check for invalid assignments - using _assignment_set = typename detail::make_set_if::type; - static_assert(_assignment_set::size::value == sizeof...(Assignment), "at least one argument is not an assignment in set()"); + using _assignment_set = typename detail::make_set_if::type; + static_assert(_assignment_set::size::value == sizeof...(Assignments), "at least one argument is not an assignment in set()"); // check for prohibited assignments - using _prohibited_assignment_set = typename detail::make_set_if::type; + using _prohibited_assignment_set = typename detail::make_set_if::type; static_assert(_prohibited_assignment_set::size::value == 0, "at least one assignment is prohibited by its column definition in set()"); - using _is_insert_list = std::true_type; - - insert_list_t(Assignment... assignment): + insert_list_t(Assignments... assignment): _columns({assignment._lhs}...), _values(assignment._rhs...) {} @@ -77,18 +102,54 @@ namespace sqlpp insert_list_t& operator=(insert_list_t&&) = default; ~insert_list_t() = default; + template + void add(Assignment&& assignment) + { + static_assert(is_assignment_t::type>::value, "set() arguments require to be assigments"); + static_assert(not ProhibitPredicate::type>::value, "set() argument must not be used in insert"); + _dynamic_columns.push_back(std::forward(assignment._lhs)); + _dynamic_values.push_back(std::forward(assignment._rhs)); + } + template void serialize(std::ostream& os, Db& db) const { - os << " ("; - detail::serialize_tuple(os, db, _columns, ','); - os << ") VALUES ("; - detail::serialize_tuple(os, db, _values, ','); - os << ")"; + if (sizeof...(Assignments) + _dynamic_columns.size()) + detail::serialize_empty_insert_list(os, db); + else + { + os << " ("; + detail::serialize_tuple(os, db, _columns, ','); + { + bool first = sizeof...(Assignments) == 0; + for (const auto column : _dynamic_columns) + { + if (not first) + os << ','; + column.serialize(os, db); + first = false; + } + } + os << ") VALUES ("; + detail::serialize_tuple(os, db, _values, ','); + { + bool first = sizeof...(Assignments) == 0; + for (const auto column : _dynamic_values) + { + if (not first) + os << ','; + column.serialize(os, db); + first = false; + } + } + os << ")"; + } } - std::tuple...> _columns; - std::tuple _values; + std::tuple...> _columns; + std::tuple _values; + typename detail::dynamic_column_list::type _dynamic_columns; + typename detail::dynamic_column_list::type _dynamic_values; }; } diff --git a/include/sqlpp11/remove.h b/include/sqlpp11/remove.h index 7e48f804..da6928e6 100644 --- a/include/sqlpp11/remove.h +++ b/include/sqlpp11/remove.h @@ -36,6 +36,7 @@ namespace sqlpp { template< + typename Database, typename Table, typename Using = noop, typename Where = noop @@ -43,6 +44,7 @@ namespace sqlpp struct remove_t; template< + typename Database, typename Table, typename Using, typename Where @@ -53,13 +55,14 @@ namespace sqlpp static_assert(is_noop::value or is_using_t::value, "invalid 'Using' argument"); static_assert(is_noop::value or is_where_t::value, "invalid 'Where' argument"); - template - using set_using_t = remove_t::type...>, Where>; - template - using set_where_t = remove_t::type>>; + template + using set_using_t = remove_t; + template + using set_where_t = remove_t; template - set_using_t using_(Tab&&... tab) + auto using_(Tab&&... tab) + -> set_using_t::type...>> { static_assert(std::is_same::value, "cannot call using() twice"); static_assert(std::is_same::value, "cannot call using() after where()"); @@ -70,8 +73,28 @@ namespace sqlpp }; } + auto dynamic_using_() + -> set_using_t> + { + static_assert(std::is_same::value, "cannot call using() twice"); + static_assert(std::is_same::value, "cannot call using() after where()"); + return { + _table, + {{}}, + _where + }; + } + + template + void add_using_(Tab&& table) + { + static_assert(is_dynamic_t::value, "cannot call add_using() in a non-dynamic using"); + _using.add(std::forward(table)); + } + template - set_where_t where(Expr&& where) + auto where(Expr&& where) + -> set_where_t::type>> { static_assert(std::is_same::value, "cannot call where() twice"); return { @@ -81,6 +104,26 @@ namespace sqlpp }; } + auto dynamic_where() + -> set_where_t> + { + static_assert(std::is_same::value, "cannot call where() twice"); + return { + _table, + _using, + {{}}, + }; + } + + template + void add_where(Expr&& expr) + { + static_assert(is_dynamic_t::value, "cannot call add_where() in a non-dynamic where"); + + _where.add(std::forward(expr)); + } + + template const remove_t& serialize(std::ostream& os, Db& db) const { @@ -112,7 +155,13 @@ namespace sqlpp }; template - constexpr remove_t::type> remove_from(Table&& table) + constexpr remove_t::type> remove_from(Table&& table) + { + return {std::forward
(table)}; + } + + template + constexpr remove_t::type, typename std::decay
::type> dynamic_remove_from(Db&& db, Table&& table) { return {std::forward
(table)}; } diff --git a/include/sqlpp11/update.h b/include/sqlpp11/update.h index d4f45474..3a12b9ef 100644 --- a/include/sqlpp11/update.h +++ b/include/sqlpp11/update.h @@ -36,6 +36,7 @@ namespace sqlpp { template< + typename Database = void, typename Table = noop, typename Assignments = noop, typename Where = noop @@ -43,6 +44,7 @@ namespace sqlpp struct update_t; template< + typename Database, typename Table, typename Assignments, typename Where @@ -53,13 +55,14 @@ namespace sqlpp static_assert(is_noop::value or is_assignment_list_t::value, "invalid 'Assignments' arguments"); static_assert(is_noop::value or is_where_t::value, "invalid 'Where' argument"); - template - using set_assignments_t = update_t::type...>, Where>; - template - using set_where_t = update_t::type>>; + template + using set_assignments_t = update_t; + template + using set_where_t = update_t; template - set_assignments_t set(Assignment&&... assignment) + auto set(Assignment&&... assignment) + -> set_assignments_t::type...>> { static_assert(std::is_same::value, "cannot call set() twice"); return { @@ -69,8 +72,29 @@ namespace sqlpp }; } + template + auto dynamic_set(Assignment&&... assignment) + -> set_assignments_t::type...>> + { + static_assert(std::is_same::value, "cannot call set() twice"); + return { + _table, + {std::tuple::type...>{std::forward(assignment)...}}, + _where, + }; + } + + template + void add_set(Assignment&& assignment) + { + static_assert(is_dynamic_t::value, "cannot call add_set() in a non-dynamic set"); + + _assignments.add(std::forward(assignment)); + } + template - set_where_t where(Expr&& where) + auto where(Expr&& where) + -> set_where_t::type>> { static_assert(not std::is_same::value, "cannot call where() if set() hasn't been called yet"); static_assert(std::is_same::value, "cannot call where() twice"); @@ -81,6 +105,26 @@ namespace sqlpp }; } + auto dynamic_where() + -> set_where_t> + { + static_assert(not std::is_same::value, "cannot call where() if set() hasn't been called yet"); + static_assert(std::is_same::value, "cannot call where() twice"); + return { + _table, + _assignments, + {{}}, + }; + } + + template + void add_where(Expr&& expr) + { + static_assert(is_dynamic_t::value, "cannot call add_where() in a non-dynamic where"); + + _where.add(std::forward(expr)); + } + template const update_t& serialize(std::ostream& os, Db& db) const { @@ -112,7 +156,13 @@ namespace sqlpp }; template - constexpr update_t::type> update(Table&& table) + constexpr update_t::type> update(Table&& table) + { + return {std::forward
(table)}; + } + + template + constexpr update_t::type, typename std::decay
::type> dynamic_update(Db&& db, Table&& table) { return {std::forward
(table)}; } diff --git a/include/sqlpp11/using.h b/include/sqlpp11/using.h index 71e1ce7a..694e056a 100644 --- a/include/sqlpp11/using.h +++ b/include/sqlpp11/using.h @@ -27,9 +27,11 @@ #ifndef SQLPP_USING_H #define SQLPP_USING_H -#include -#include #include +#include +#include +#include +#include namespace sqlpp { @@ -38,7 +40,15 @@ namespace sqlpp { using _is_using = std::true_type; - static_assert(sizeof...(Table), "at least one table argument required in using"); + static_assert(sizeof...(Table), "at least one table argument required in using()"); + + // check for duplicate arguments + static_assert(not detail::has_duplicates::value, "at least one duplicate argument detected in using()"); + + // check for invalid arguments + using _valid_expressions = typename detail::make_set_if::type; + static_assert(_valid_expressions::size::value == sizeof...(Table), "at least one argument is not an table in using()"); + template void serialize(std::ostream& os, Db& db) const @@ -49,6 +59,37 @@ namespace sqlpp std::tuple _tables; }; + + template + struct dynamic_using_t + { + using _is_using = std::true_type; + using _is_dynamic = std::true_type; + + template + void add(Table&& table) + { + static_assert(is_table_t::type>::value, "using arguments require to be tables"); + _dynamic_tables.push_back(std::forward
(table)); + } + + void serialize(std::ostream& os, Db& db) const + { + if (_dynamic_tables.empty()) + return; + os << " USING "; + bool first = true; + for (const auto& table : _dynamic_tables) + { + if (not first) + os << ','; + table.serialize(os, db); + first = false; + } + } + + std::vector> _dynamic_tables; + }; } #endif diff --git a/tests/InsertTest.cpp b/tests/InsertTest.cpp index 2b84c4c0..681538e3 100644 --- a/tests/InsertTest.cpp +++ b/tests/InsertTest.cpp @@ -47,6 +47,9 @@ int main() insert_into(t).serialize(std::cerr, db); std::cerr << "\n"; insert_into(t).set(t.beta = "kirschauflauf").serialize(std::cerr, db); std::cerr << "\n"; + auto i = dynamic_insert_into(db, t).dynamic_set(); + i.add_set(t.beta = "kirschauflauf"); + i.serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).values(7, "wurstwaren", true).serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).values(25, "drei").serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).select(select(t.alpha, t.beta).from(t)).serialize(std::cerr, db); diff --git a/tests/RemoveTest.cpp b/tests/RemoveTest.cpp index 6e548841..118d95f9 100644 --- a/tests/RemoveTest.cpp +++ b/tests/RemoveTest.cpp @@ -48,6 +48,10 @@ int main() remove_from(t).serialize(std::cerr, db); std::cerr << "\n"; remove_from(t).where(t.beta != "transparent").serialize(std::cerr, db); std::cerr << "\n"; remove_from(t).using_(t).serialize(std::cerr, db); std::cerr << "\n"; + auto r = dynamic_remove_from(db, t).dynamic_using_().dynamic_where(); + r.add_using_(t); + r.add_where(t.beta != "transparent"); + r.serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).values(7, "wurstwaren", true).serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).values(25, "drei").serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).select(select(t.alpha, t.beta).from(t)).serialize(std::cerr, db); diff --git a/tests/UpdateTest.cpp b/tests/UpdateTest.cpp index 3d32b106..beb242be 100644 --- a/tests/UpdateTest.cpp +++ b/tests/UpdateTest.cpp @@ -48,6 +48,10 @@ int main() update(t).serialize(std::cerr, db); std::cerr << "\n"; update(t).set(t.gamma = false).serialize(std::cerr, db); std::cerr << "\n"; update(t).set(t.gamma = false).where(t.beta != "transparent").serialize(std::cerr, db); std::cerr << "\n"; + auto u = dynamic_update(db, t).dynamic_set(t.gamma = false).dynamic_where(); + u.add_set(t.gamma = false); + u.serialize(std::cerr, db); std::cerr << "\n"; + //insert_into(t).values(7, "wurstwaren", true).serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).values(25, "drei").serialize(std::cerr, db); std::cerr << "\n"; //insert_into(t).columns(t.alpha, t.beta).select(select(t.alpha, t.beta).from(t)).serialize(std::cerr, db);