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:
parent
b869cbe5b2
commit
c70ceaceb0
@ -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>>
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user