0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-16 12:51:13 +08:00

Introduce optional select columns

Basic functionality seems to work, more tests needed.
This commit is contained in:
Roland Bock 2024-06-23 11:23:31 +02:00
parent b869cbe5b2
commit c70ceaceb0
7 changed files with 136 additions and 30 deletions

View File

@ -86,6 +86,11 @@ namespace sqlpp
return {*this}; return {*this};
} }
sqlpp::compat::optional<column_t> if_(bool condition) const
{
return condition ? sqlpp::compat::make_optional(*this) : sqlpp::compat::nullopt;
}
template <typename T> template <typename T>
auto operator=(T t) const -> assignment_t<column_t, wrap_operand_t<T>> auto operator=(T t) const -> assignment_t<column_t, wrap_operand_t<T>>
{ {

View File

@ -45,6 +45,8 @@ namespace sqlpp
using std::nullopt; using std::nullopt;
using std::bad_optional_access; using std::bad_optional_access;
using std::make_optional;
} // namespace compat } // namespace compat
} // namespace sqlpp } // namespace sqlpp
@ -249,6 +251,12 @@ namespace sqlpp
return !right; return !right;
} }
template <typename T>
constexpr optional<typename std::decay<T>::type> make_optional(T&& value)
{
return optional<typename std::decay<T>::type>(std::forward<T>(value));
}
} // namespace compat } // namespace compat
} // namespace sqlpp } // namespace sqlpp

View File

@ -76,13 +76,14 @@ namespace sqlpp
template <typename Select, typename NamedExpr> template <typename Select, typename NamedExpr>
struct make_field_spec_impl struct make_field_spec_impl
{ {
static constexpr bool _can_be_null = can_be_null_t<NamedExpr>::value; using RawNamedExpr = remove_optional_t<NamedExpr>;
static constexpr bool _can_be_null = is_optional<NamedExpr>::value or can_be_null_t<RawNamedExpr>::value;
static constexpr bool _depends_on_outer_table = static constexpr bool _depends_on_outer_table =
detail::make_intersect_set_t<required_tables_of<NamedExpr>, detail::make_intersect_set_t<required_tables_of<RawNamedExpr>,
typename Select::_used_outer_tables>::size::value > 0; typename Select::_used_outer_tables>::size::value > 0;
using type = field_spec_t<typename NamedExpr::_alias_t, using type = field_spec_t<typename RawNamedExpr::_alias_t,
value_type_of<NamedExpr>, value_type_of<RawNamedExpr>,
logic::any_t<_can_be_null, _depends_on_outer_table>::value>; logic::any_t<_can_be_null, _depends_on_outer_table>::value>;
}; };
} // namespace detail } // namespace detail

View File

@ -55,36 +55,44 @@ namespace sqlpp
template <typename Column> template <typename Column>
struct select_traits<Column> struct select_traits<Column>
{ {
using _traits = make_traits<value_type_of<Column>, using _traits = make_traits<value_type_of<remove_optional_t<Column>>,
tag::is_select_column_list, tag::is_select_column_list,
tag::is_return_value, tag::is_return_value,
tag::is_expression, tag::is_expression,
tag::is_selectable>; tag::is_selectable>;
using _alias_t = typename Column::_alias_t; using _alias_t = typename remove_optional_t<Column>::_alias_t;
}; };
} // namespace detail } // namespace detail
// SELECTED COLUMNS DATA template <typename Column>
template <typename... Columns> struct select_column_t
struct select_column_list_data_t
{ {
select_column_list_data_t(Columns... columns) : _columns(columns...) constexpr select_column_t(Column column) : _column(column)
{ {
} }
select_column_list_data_t(std::tuple<Columns...> columns) : _columns(columns) 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;
select_column_list_data_t(const select_column_list_data_t&) = default; Column _column;
select_column_list_data_t(select_column_list_data_t&&) = default;
select_column_list_data_t& operator=(const select_column_list_data_t&) = default;
select_column_list_data_t& operator=(select_column_list_data_t&&) = default;
~select_column_list_data_t() = default;
std::tuple<Columns...> _columns;
}; };
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( SQLPP_PORTABLE_STATIC_ASSERT(
assert_no_unknown_tables_in_selected_columns_t, assert_no_unknown_tables_in_selected_columns_t,
"at least one selected column requires a table which is otherwise not known in the statement"); "at least one selected column requires a table which is otherwise not known in the statement");
@ -103,7 +111,7 @@ namespace sqlpp
using _alias_t = typename detail::select_traits<Columns...>::_alias_t; using _alias_t = typename detail::select_traits<Columns...>::_alias_t;
using _data_t = select_column_list_data_t<Columns...>; using _data_t = std::tuple<select_column_t<Columns>...>;
struct _column_type struct _column_type
{ {
@ -231,8 +239,9 @@ namespace sqlpp
template <typename... T> template <typename... T>
struct check_selected_columns struct check_selected_columns
{ {
using type = static_combined_check_t< using type =
static_check_t<logic::all_t<is_selectable_t<T>::value...>::value, assert_selected_colums_are_selectable_t>>; static_combined_check_t<static_check_t<logic::all_t<is_selectable_t<remove_optional_t<T>>::value...>::value,
assert_selected_colums_are_selectable_t>>;
}; };
template <typename... T> template <typename... T>
using check_selected_columns_t = typename check_selected_columns<T...>::type; using check_selected_columns_t = typename check_selected_columns<T...>::type;
@ -299,9 +308,9 @@ namespace sqlpp
// Interpreters // Interpreters
template <typename Context, typename... Columns> template <typename Context, typename... Columns>
Context& serialize(const select_column_list_data_t<Columns...>& t, Context& context) Context& serialize(const std::tuple<select_column_t<Columns>...>& t, Context& context)
{ {
interpret_tuple(t._columns, ',', context); interpret_tuple(t, ',', context);
return context; return context;
} }

View File

@ -28,6 +28,8 @@
#include <type_traits> #include <type_traits>
#include <tuple> #include <tuple>
#include <sqlpp11/compat/optional.h>
#include <sqlpp11/consistent.h> #include <sqlpp11/consistent.h>
#include <sqlpp11/portable_static_assert.h> #include <sqlpp11/portable_static_assert.h>
#include <sqlpp11/detail/type_vector.h> #include <sqlpp11/detail/type_vector.h>
@ -37,6 +39,55 @@
namespace sqlpp namespace sqlpp
{ {
template <typename T>
struct is_optional : public std::false_type
{
};
template <typename T>
struct is_optional<sqlpp::compat::optional<T>> : public std::true_type
{
};
template <typename T>
struct remove_optional
{
using type = T;
};
template <typename T>
struct remove_optional<sqlpp::compat::optional<T>>
{
using type = T;
};
template <typename T>
const T& get_value(const T& t)
{
return t;
}
template <typename T>
const T& get_value(const sqlpp::compat::optional<T>& t)
{
return t.value();
}
template <typename T>
auto has_value(const T&) -> bool
{
return true;
}
template <typename T>
auto has_value(const sqlpp::compat::optional<T>& t) -> bool
{
return t.has_value();
}
template <typename T>
using remove_optional_t = typename remove_optional<T>::type;
struct no_value_t; struct no_value_t;
namespace detail namespace detail
{ {

View File

@ -39,21 +39,37 @@ int SelectColumns(int, char*[])
compare(__LINE__, select(foo.doubleN, bar.id), "SELECT tab_foo.double_n,tab_bar.id"); compare(__LINE__, select(foo.doubleN, bar.id), "SELECT tab_foo.double_n,tab_bar.id");
// All columns of a table // All columns of a table
compare(__LINE__, select(all_of(foo)), "SELECT tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n"); compare(__LINE__, select(all_of(foo)),
"SELECT tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n");
// All columns of a table plus one more // All columns of a table plus one more
compare(__LINE__, select(all_of(foo), bar.id), "SELECT tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n,tab_bar.id"); compare(
__LINE__, select(all_of(foo), bar.id),
"SELECT tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n,tab_bar.id");
// One more, plus all columns of a table // One more, plus all columns of a table
compare(__LINE__, select(bar.id, all_of(foo)), "SELECT tab_bar.id,tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n"); compare(
__LINE__, select(bar.id, all_of(foo)),
"SELECT tab_bar.id,tab_foo.id,tab_foo.text_nn_d,tab_foo.int_n,tab_foo.double_n,tab_foo.u_int_n,tab_foo.blob_n");
// Column and aggregate function // Column and aggregate function
compare(__LINE__, select(foo.doubleN, count(bar.id)), "SELECT tab_foo.double_n,COUNT(tab_bar.id) AS count_"); compare(__LINE__, select(foo.doubleN, count(bar.id)), "SELECT tab_foo.double_n,COUNT(tab_bar.id) AS count_");
// Column aliases // Column aliases
compare(__LINE__, select(foo.doubleN.as(sqlpp::alias::o), count(bar.id).as(sqlpp::alias::a)), "SELECT tab_foo.double_n AS o,COUNT(tab_bar.id) AS a"); compare(__LINE__, select(foo.doubleN.as(sqlpp::alias::o), count(bar.id).as(sqlpp::alias::a)),
"SELECT tab_foo.double_n AS o,COUNT(tab_bar.id) AS a");
#warning: add optional column tests // Optional column manually
compare(__LINE__, select(sqlpp::compat::make_optional(bar.id)), "SELECT tab_bar.id");
auto opt_id = sqlpp::compat::make_optional(bar.id);
opt_id.reset();
compare(__LINE__, select(opt_id), "SELECT NULL AS id");
// Optional column using `if_`
compare(__LINE__, select(bar.id.if_(true)), "SELECT tab_bar.id");
compare(__LINE__, select(bar.id.if_(false)), "SELECT NULL AS id");
#warning: add more optional column tests
return 0; return 0;
} }

View File

@ -55,6 +55,21 @@ namespace
} }
} }
void optional_columns()
{
{
// result fields are as nullable as the expressions they represent
#warning: add `if_` to other expressions, too
#warning: test with nullable columns, too.
#warning: test with all kinds of functions as well.
#warning: We should actually test for the exact type!
const auto rows = db(select(bar.id.if_(true), bar.boolNn.if_(true)/*, seven*/).from(bar).unconditionally());
auto& x = rows.front();
static_assert(is_optional<decltype(x.id)>::value, "");
static_assert(is_optional<decltype(x.boolNn)>::value, "");
}
}
void join() void join()
{ {
// Join // Join
@ -233,6 +248,7 @@ namespace
int main(int, char* []) int main(int, char* [])
{ {
single_table(); single_table();
optional_columns();
join(); join();
aggregates(); aggregates();
} }