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};
}
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 t) const -> assignment_t<column_t, wrap_operand_t<T>>
{

View File

@ -45,6 +45,8 @@ namespace sqlpp
using std::nullopt;
using std::bad_optional_access;
using std::make_optional;
} // namespace compat
} // namespace sqlpp
@ -249,6 +251,12 @@ namespace sqlpp
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 sqlpp

View File

@ -76,13 +76,14 @@ namespace sqlpp
template <typename Select, typename NamedExpr>
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 =
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;
using type = field_spec_t<typename NamedExpr::_alias_t,
value_type_of<NamedExpr>,
using type = field_spec_t<typename RawNamedExpr::_alias_t,
value_type_of<RawNamedExpr>,
logic::any_t<_can_be_null, _depends_on_outer_table>::value>;
};
} // namespace detail

View File

@ -55,36 +55,44 @@ namespace sqlpp
template <typename 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_return_value,
tag::is_expression,
tag::is_selectable>;
using _alias_t = typename Column::_alias_t;
using _alias_t = typename remove_optional_t<Column>::_alias_t;
};
} // namespace detail
// SELECTED COLUMNS DATA
template <typename... Columns>
struct select_column_list_data_t
template <typename Column>
struct select_column_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;
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;
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");
@ -103,7 +111,7 @@ namespace sqlpp
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
{
@ -231,8 +239,9 @@ namespace sqlpp
template <typename... T>
struct check_selected_columns
{
using type = static_combined_check_t<
static_check_t<logic::all_t<is_selectable_t<T>::value...>::value, assert_selected_colums_are_selectable_t>>;
using type =
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>
using check_selected_columns_t = typename check_selected_columns<T...>::type;
@ -299,9 +308,9 @@ namespace sqlpp
// Interpreters
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;
}

View File

@ -28,6 +28,8 @@
#include <type_traits>
#include <tuple>
#include <sqlpp11/compat/optional.h>
#include <sqlpp11/consistent.h>
#include <sqlpp11/portable_static_assert.h>
#include <sqlpp11/detail/type_vector.h>
@ -37,6 +39,55 @@
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;
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");
// 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
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
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
compare(__LINE__, select(foo.doubleN, count(bar.id)), "SELECT tab_foo.double_n,COUNT(tab_bar.id) AS count_");
// 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;
}

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()
{
// Join
@ -233,6 +248,7 @@ namespace
int main(int, char* [])
{
single_table();
optional_columns();
join();
aggregates();
}