0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-16 04:47:18 +08:00

Introduce dynamic

This allows to select columns dynamically
This commit is contained in:
Roland Bock 2024-07-18 07:38:07 +02:00
parent 25200ba4cb
commit 700c263f90
9 changed files with 145 additions and 182 deletions

View File

@ -38,4 +38,27 @@ SQLPP_ALIAS_PROVIDER(max_price);
}
```
# Dynamic queries
We don't always have a completely fixed structure for our queries. For instance, there might columns that we only want to select under certain circumstances. In version 1.0, this was handled by dynamic queries. Now we introduce conditional query parts that may or may not be used at runtime:
## Select optional columns
select(tab.id, dynamic(condition, tab.bigData)).from(tab).where(tab.id == 17);
If `condition == true` then `bigData` will be selected, otherwise `NULL` will be selected.
## Join optional table
select(tabA.id).from(tabA.cross_join(dynamic(condition, tabB))).where(tab.id == 17);
If `condition == true` then the cross join will be part of the query, otherwise not. Obviously, that means that you need to make sure that query parts that rely on `tabB` in this example also depend on the same condition.
## Optional AND operand
select(tab.id).from(tab).where(tab.id == 17 and dynamic(condition, tab.desert != "cheesecake"));
If `condition == true`, then the dynamic part will evaluate to `tab.desert != "cheesecake")`. Otherwise it will be treated as `true` (and the AND expression will be collapsed).
## Optional OR operand
select(tab.id).from(tab).where(tab.id == 17 or dynamic(condition, tab.desert != "cheesecake"));
If `condition == true`, then the dynamic part will evaluate to `tab.desert != "cheesecake")`. Otherwise it will be treated as `false` (and the OR expression will be collapsed).

View File

@ -1,62 +0,0 @@
#pragma once
/*
* Copyright (c) 2013-2015, 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 <sqlpp11/operator/as_expression.h>
namespace sqlpp
{
template <typename T, typename Enable = void>
struct has_auto_alias_t
{
static constexpr bool value = false;
};
template <typename T>
struct has_auto_alias_t<T, typename std::enable_if<not wrong_t<typename T::_auto_alias_t>::value>::type>
{
static constexpr bool value = true;
};
namespace detail
{
template <typename T, typename Enable = void>
struct auto_alias_impl
{
using type = T;
};
template <typename T>
struct auto_alias_impl<T, typename std::enable_if<has_auto_alias_t<T>::value>::type>
{
using type = as_expression<T, typename T::_auto_alias_t>;
};
} // namespace detail
template <typename T>
using auto_alias_t = typename detail::auto_alias_impl<T>::type;
} // namespace sqlpp

View File

@ -71,12 +71,6 @@ namespace sqlpp
return _table{};
}
#warning: Let's do if_(condition, expression) -> if_t<Expression>, which can be used exclusively in SELECT, AND, OR, JOIN
sqlpp::compat::optional<column_t> if_(bool condition) const
{
return condition ? sqlpp::compat::make_optional(*this) : sqlpp::compat::nullopt;
}
template <typename T>
auto operator=(T value) const -> assign_expression<column_t, T>
{

View File

@ -1,55 +0,0 @@
#pragma once
/*
* Copyright (c) 2021-2021, 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 <tuple>
#include <sqlpp11/auto_alias.h>
namespace sqlpp
{
namespace detail
{
template <typename T>
std::tuple<auto_alias_t<T>> as_column_tuple(T t)
{
return std::tuple<auto_alias_t<T>>(auto_alias_t<T>{t});
}
template <typename... Args>
std::tuple<auto_alias_t<Args>...> as_column_tuple(std::tuple<Args...> t)
{
return t;
}
template <typename... Columns>
auto column_tuple_merge(Columns... columns) -> decltype(std::tuple_cat(as_column_tuple(columns)...))
{
return std::tuple_cat(as_column_tuple(columns)...);
}
}
} // namespace sqlpp

109
include/sqlpp11/dynamic.h Normal file
View File

@ -0,0 +1,109 @@
#pragma once
/*
* Copyright (c) 2024, 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 <sqlpp11/type_traits.h>
#include <sqlpp11/serialize.h>
namespace sqlpp
{
template <typename Expr>
struct dynamic_t
{
using _traits = make_traits<value_type_of_t<Expr>, tag::is_multi_expression>;
using _nodes = detail::type_vector<Expr>;
dynamic_t(bool condition, Expr expr) : _condition(condition), _expr(expr)
{
}
dynamic_t(const dynamic_t&) = default;
dynamic_t(dynamic_t&&) = default;
dynamic_t& operator=(const dynamic_t&) = default;
dynamic_t& operator=(dynamic_t&&) = default;
~dynamic_t() = default;
bool _condition;
Expr _expr;
};
// No value_type_of defined for dynamic_t, because it is to be used in very specific contexts in which _expr may be
// used depending on the value of _condition.
template <typename T>
struct remove_dynamic
{
using type = T;
};
template <typename Expr>
struct remove_dynamic<dynamic_t<Expr>>
{
using type = Expr;
};
template <typename T>
using remove_dynamic_t = typename remove_dynamic<T>::type;
template <typename T>
struct dynamic_to_optional
{
using type = T;
};
template <typename Expr>
struct dynamic_to_optional<dynamic_t<Expr>>
{
using type = force_optional_t<Expr>;
};
template <typename T>
using dynamic_to_optional_t = typename dynamic_to_optional<T>::type;
template <typename Context, typename Select>
Context& serialize(const dynamic_t<Select>& t, Context& context)
{
if (t._condition)
{
serialize(t._expr, context);
}
else
{
context << NULL;
}
return context;
}
template <typename Expr>
using check_dynamic_args = std::enable_if_t<has_value_type<Expr>::value>;
template <typename Expr, typename = check_dynamic_args<Expr>>
auto dynamic(bool condition, Expr t) -> dynamic_t<Expr>
{
return {condition, std::move(t)};
}
} // namespace sqlpp

View File

@ -26,6 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sqlpp11/dynamic.h>
#include <sqlpp11/noop.h>
#include <sqlpp11/parameter.h>
#include <sqlpp11/parameter_list.h>

View File

@ -47,11 +47,6 @@ namespace sqlpp
using _alias_t = typename AliasProvider::_alias_t;
sqlpp::compat::optional<as_expression> if_(bool condition) const
{
return condition ? sqlpp::compat::make_optional(*this) : sqlpp::compat::nullopt;
}
as_expression(Expression expression) : _expression(expression)
{
}

View File

@ -26,9 +26,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sqlpp11/auto_alias.h>
#include <sqlpp11/detail/column_tuple_merge.h>
#include <sqlpp11/detail/type_set.h>
#include <sqlpp11/dynamic.h>
#include <sqlpp11/expression_fwd.h>
#include <sqlpp11/field_spec.h>
#include <sqlpp11/interpret_tuple.h>
@ -63,35 +62,6 @@ namespace sqlpp
};
} // namespace detail
template <typename Column>
struct select_column_t
{
constexpr select_column_t(Column column) : _column(column)
{
}
constexpr select_column_t(const select_column_t&) = default;
constexpr select_column_t(select_column_t&&) = default;
select_column_t& operator=(const select_column_t&) = default;
select_column_t& operator=(select_column_t&&) = default;
~select_column_t() = default;
Column _column;
};
template <typename Context, typename Column>
Context& serialize(const select_column_t<Column>& t, Context& context)
{
if (has_value(t._column))
{
return serialize(get_value(t._column), context);
}
context << "NULL AS " << name_of<remove_optional_t<Column>>::template char_ptr<Context>();
return context;
}
SQLPP_PORTABLE_STATIC_ASSERT(
assert_no_unknown_tables_in_selected_columns_t,
"at least one selected column requires a table which is otherwise not known in the statement");
@ -110,7 +80,7 @@ namespace sqlpp
using _alias_t = typename detail::select_traits<Columns...>::_alias_t;
using _data_t = std::tuple<select_column_t<Columns>...>;
using _data_t = std::tuple<Columns...>;
struct _column_type
{
@ -171,7 +141,7 @@ namespace sqlpp
using _field_t = typename _deferred_field_t<Db, Column>::type;
template <typename Db>
using _result_row_t = result_row_t<Db, _field_t<Db, Columns>...>;
using _result_row_t = result_row_t<Db, _field_t<Db, dynamic_to_optional_t<Columns>>...>;
template <typename AliasProvider>
struct _deferred_table_t
@ -231,18 +201,12 @@ namespace sqlpp
template <typename Column>
struct value_type_of<select_column_list_t<Column>> : public value_type_of<Column> {};
namespace detail
{
template <typename... Columns>
select_column_list_t<Columns...> make_column_list(std::tuple<Columns...> columns);
} // namespace detail
SQLPP_PORTABLE_STATIC_ASSERT(assert_selected_colums_are_selectable_t, "selected columns must be selectable");
template <typename... T>
struct check_selected_columns
{
using type =
static_combined_check_t<static_check_t<logic::all_t<is_selectable_t<remove_optional_t<T>>::value...>::value,
static_combined_check_t<static_check_t<logic::all_t<is_selectable_t<T>::value...>::value,
assert_selected_colums_are_selectable_t>>;
};
template <typename... T>
@ -270,12 +234,6 @@ namespace sqlpp
_data_t _data;
template <typename... T>
static constexpr auto _check_args(std::tuple<T...> /*args*/) -> check_selected_columns_t<T...>
{
return {};
}
template <typename Check, typename T>
using _new_statement_t = new_statement_t<Check, Policies, no_select_column_list_t, T>;
@ -283,15 +241,15 @@ namespace sqlpp
template <typename... Args>
auto columns(Args... args) const
-> _new_statement_t<decltype(_check_args(detail::column_tuple_merge(args...))),
decltype(detail::make_column_list(detail::column_tuple_merge(args...)))>
-> _new_statement_t<check_selected_columns_t<remove_dynamic_t<Args>...>,
select_column_list_t<Args...>>
{
static_assert(sizeof...(Args), "at least one selectable expression (e.g. a column) required in columns()");
using check = decltype(_check_args(detail::column_tuple_merge(args...)));
using check = check_selected_columns_t<remove_dynamic_t<Args>...>;
static_assert(check::value,
"at least one argument is not a selectable expression in columns()");
return _columns_impl(check{}, detail::column_tuple_merge(args...));
return _columns_impl(check{}, std::make_tuple(std::move(args)...));
}
private:
@ -303,14 +261,14 @@ namespace sqlpp
-> _new_statement_t<consistent_t, select_column_list_t<Args...>>
{
return {static_cast<const derived_statement_t<Policies>&>(*this),
typename select_column_list_t<Args...>::_data_t{args}};
typename select_column_list_t<Args...>::_data_t{std::move(args)}};
}
};
};
// Interpreters
template <typename Context, typename... Columns>
Context& serialize(const std::tuple<select_column_t<Columns>...>& t, Context& context)
Context& serialize(const std::tuple<Columns...>& t, Context& context)
{
interpret_tuple(t, ',', context);
return context;

View File

@ -57,8 +57,8 @@ void test_result_row(Value v)
const auto v_maybe_null = sqlpp::value(sqlpp::compat::make_optional(v)).as(r_maybe_null);
// Optional selectable values.
const auto v_opt_not_null = sqlpp::value(v).as(r_opt_not_null).if_(true);
const auto v_opt_maybe_null = sqlpp::value(sqlpp::compat::make_optional(v)).as(r_opt_maybe_null).if_(true);
const auto v_opt_not_null = dynamic(true, sqlpp::value(v).as(r_opt_not_null));
const auto v_opt_maybe_null = dynamic(true, sqlpp::value(sqlpp::compat::make_optional(v)).as(r_opt_maybe_null));
for (const auto& row : db(select(v_not_null, v_maybe_null, v_opt_not_null, v_opt_maybe_null)))
{