mirror of
https://github.com/rbock/sqlpp11.git
synced 2024-11-16 04:47:18 +08:00
More type tests
This commit is contained in:
parent
25faf6c4bb
commit
4630bf7f0c
@ -78,22 +78,6 @@ namespace sqlpp
|
||||
template <typename T>
|
||||
using remove_dynamic_t = typename remove_dynamic<T>::type;
|
||||
|
||||
#warning: Should not turn Expr to into optional, but just the value 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(Context& context, const dynamic_t<Select>& t)
|
||||
{
|
||||
|
@ -26,17 +26,16 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sqlpp11/type_traits.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <sqlpp11/compat/optional.h>
|
||||
#include <sqlpp11/type_traits.h>
|
||||
#include <sqlpp11/dynamic.h>
|
||||
|
||||
namespace sqlpp
|
||||
{
|
||||
#warning: Do we need this? It should be possible to use name_tag_of_t and value_type_of_t somehow
|
||||
template <typename NameType, typename ValueType>
|
||||
struct field_spec_t
|
||||
struct field_spec_t : public name_tag_base
|
||||
{
|
||||
using _alias_t = NameType;
|
||||
|
||||
@ -77,17 +76,22 @@ namespace sqlpp
|
||||
template <typename Select, typename NamedExpr>
|
||||
struct make_field_spec_impl
|
||||
{
|
||||
#warning: required_tables_of_t and obtaining the alias should handle optional.
|
||||
#warning: remove_optional should not be necessary at all, since we are using dynamic instead
|
||||
using RawNamedExpr = remove_optional_t<NamedExpr>;
|
||||
using ValueType = value_type_of_t<NamedExpr>;
|
||||
static constexpr bool _depends_on_outer_table =
|
||||
detail::make_intersect_set_t<required_tables_of_t<RawNamedExpr>,
|
||||
detail::make_intersect_set_t<required_tables_of_t<NamedExpr>,
|
||||
typename Select::_used_outer_tables>::size::value > 0;
|
||||
using ValueType = typename std::conditional<_depends_on_outer_table,
|
||||
sqlpp::force_optional_t<value_type_of_t<NamedExpr>>,
|
||||
value_type_of_t<NamedExpr>>::type;
|
||||
|
||||
using type = field_spec_t<name_tag_of_t<RawNamedExpr>,
|
||||
using type = field_spec_t<
|
||||
name_tag_of_t<NamedExpr>,
|
||||
typename std::conditional<_depends_on_outer_table, sqlpp::force_optional_t<ValueType>, ValueType>::type>;
|
||||
};
|
||||
|
||||
template <typename Select, typename NamedExpr>
|
||||
struct make_field_spec_impl<Select, dynamic_t<NamedExpr>>
|
||||
{
|
||||
using ValueType = force_optional_t<value_type_of_t<NamedExpr>>;
|
||||
|
||||
using type = field_spec_t<name_tag_of_t<NamedExpr>,
|
||||
ValueType>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
@ -155,7 +155,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, dynamic_to_optional_t<Columns>>...>;
|
||||
using _result_row_t = result_row_t<Db, _field_t<Db, Columns>...>;
|
||||
|
||||
template <typename AliasProvider>
|
||||
struct _deferred_table_t
|
||||
@ -212,25 +212,42 @@ namespace sqlpp
|
||||
};
|
||||
};
|
||||
template <typename Column>
|
||||
struct value_type_of<select_column_list_t<Column>> : public value_type_of<Column> {};
|
||||
|
||||
template<typename Column>
|
||||
struct name_tag_of<select_column_list_t<Column>> :public name_tag_of<Column>{};
|
||||
struct value_type_of<select_column_list_t<Column>> : public value_type_of<Column>
|
||||
{
|
||||
};
|
||||
template <typename Column>
|
||||
struct value_type_of<select_column_list_t<dynamic_t<Column>>>
|
||||
{
|
||||
using type = force_optional_t<value_type_of_t<Column>>;
|
||||
};
|
||||
|
||||
template <typename Column>
|
||||
struct name_tag_of<select_column_list_t<Column>> : public name_tag_of<Column>
|
||||
{
|
||||
};
|
||||
|
||||
SQLPP_PORTABLE_STATIC_ASSERT(assert_selected_colums_are_selectable_t, "selected columns must be selectable");
|
||||
|
||||
template <typename T>
|
||||
struct check_selected_columns;
|
||||
template <typename... T>
|
||||
struct check_selected_columns<std::tuple<T...>>
|
||||
struct check_selected_column : std::integral_constant<bool, has_value_type<T>::value and has_name<T>::value>
|
||||
{
|
||||
using type =
|
||||
static_combined_check_t<static_check_t<logic::all_t<is_selectable_t<remove_dynamic_t<T>>::value...>::value,
|
||||
assert_selected_colums_are_selectable_t>>;
|
||||
};
|
||||
template <typename T>
|
||||
using check_selected_columns_t = typename check_selected_columns<T>::type;
|
||||
struct check_selected_column<dynamic_t<T>> : check_selected_column<T>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct check_selected_tuple;
|
||||
template <typename... T>
|
||||
struct check_selected_tuple<std::tuple<T...>>
|
||||
{
|
||||
using type = static_combined_check_t<
|
||||
static_check_t<logic::all_t<check_selected_column<T>::value...>::value,
|
||||
assert_selected_colums_are_selectable_t>>;
|
||||
};
|
||||
template <typename T>
|
||||
using check_selected_tuple_t = typename check_selected_tuple<T>::type;
|
||||
|
||||
template <typename T>
|
||||
struct make_select_column_list;
|
||||
@ -270,11 +287,11 @@ namespace sqlpp
|
||||
using _consistency_check = consistent_t;
|
||||
|
||||
template <typename... Args>
|
||||
auto columns(Args... args) const -> _new_statement_t<check_selected_columns_t<detail::flat_tuple_t<Args...>>,
|
||||
auto columns(Args... args) const -> _new_statement_t<check_selected_tuple_t<detail::flat_tuple_t<Args...>>,
|
||||
make_select_column_list_t<detail::flat_tuple_t<Args...>>>
|
||||
{
|
||||
static_assert(sizeof...(Args), "at least one selectable expression (e.g. a column) required in columns()");
|
||||
using check = check_selected_columns_t<detail::flat_tuple_t<Args...>>;
|
||||
using check = check_selected_tuple_t<detail::flat_tuple_t<Args...>>;
|
||||
static_assert(check::value,
|
||||
"at least one argument is not a selectable expression in columns()");
|
||||
|
||||
|
@ -36,6 +36,7 @@ endfunction()
|
||||
|
||||
test_compile(any)
|
||||
test_compile(aggregate_functions)
|
||||
test_compile(arithmetic_expression)
|
||||
test_compile(comparison_expression)
|
||||
test_compile(dynamic)
|
||||
test_compile(in_expression)
|
||||
|
159
tests/core/types/arithmetic_expression.cpp
Normal file
159
tests/core/types/arithmetic_expression.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2016, 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 "MockDb.h"
|
||||
#include "Sample.h"
|
||||
#include <sqlpp11/sqlpp11.h>
|
||||
|
||||
#include "../../include/test_helpers.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename A, typename B>
|
||||
constexpr bool is_same_type()
|
||||
{
|
||||
return std::is_same<A, B>::value;
|
||||
}
|
||||
}
|
||||
|
||||
SQLPP_ALIAS_PROVIDER(r_not_null);
|
||||
SQLPP_ALIAS_PROVIDER(r_maybe_null);
|
||||
SQLPP_ALIAS_PROVIDER(r_opt_not_null);
|
||||
SQLPP_ALIAS_PROVIDER(r_opt_maybe_null);
|
||||
|
||||
template<typename Value>
|
||||
void test_arithmetic_expressions(Value v)
|
||||
{
|
||||
using ValueType = sqlpp::numeric;
|
||||
using OptValueType = sqlpp::compat::optional<sqlpp::numeric>;
|
||||
|
||||
auto value = sqlpp::value(v);
|
||||
auto opt_value = sqlpp::value(sqlpp::compat::make_optional(v));
|
||||
|
||||
// Arithmetically combining non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value + value)>, ValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value - value)>, ValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value * value)>, ValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value / value)>, ValueType>(), "");
|
||||
|
||||
// Arithmetically combining non-optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value + opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value - opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value * opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value / opt_value)>, OptValueType>(), "");
|
||||
|
||||
// Arithmetically combining optional with non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value + value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value - value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value * value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value / value)>, OptValueType>(), "");
|
||||
|
||||
// Arithmetically combining optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value + opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value - opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value * opt_value)>, OptValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value / opt_value)>, OptValueType>(), "");
|
||||
|
||||
// Same with negate.
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(-value)>, ValueType>(), "");
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(-opt_value)>, OptValueType>(), "");
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
void test_modulus_expressions(Value v)
|
||||
{
|
||||
using ValueType = sqlpp::numeric;
|
||||
using OptValueType = sqlpp::compat::optional<sqlpp::numeric>;
|
||||
|
||||
auto value = sqlpp::value(v);
|
||||
auto opt_value = sqlpp::value(sqlpp::compat::make_optional(v));
|
||||
|
||||
// Modulus combining non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value % value)>, ValueType>(), "");
|
||||
|
||||
// Modulus combining non-optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value % opt_value)>, OptValueType>(), "");
|
||||
|
||||
// Modulus combining optional with non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value % value)>, OptValueType>(), "");
|
||||
|
||||
// Modulus combining optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value % opt_value)>, OptValueType>(), "");
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
void test_concatenation_expressions(Value v)
|
||||
{
|
||||
using ValueType = sqlpp::text;
|
||||
using OptValueType = sqlpp::compat::optional<sqlpp::text>;
|
||||
|
||||
auto value = sqlpp::value(v);
|
||||
auto opt_value = sqlpp::value(sqlpp::compat::make_optional(v));
|
||||
|
||||
// Concatenating non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value + value)>, ValueType>(), "");
|
||||
|
||||
// Concatenating non-optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(value + opt_value)>, OptValueType>(), "");
|
||||
|
||||
// Concatenating optional with non-optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value + value)>, OptValueType>(), "");
|
||||
|
||||
// Concatenating optional with optional values
|
||||
static_assert(is_same_type<sqlpp::value_type_of_t<decltype(opt_value + opt_value)>, OptValueType>(), "");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// integral
|
||||
test_arithmetic_expressions(int8_t{7});
|
||||
test_arithmetic_expressions(int16_t{7});
|
||||
test_arithmetic_expressions(int32_t{7});
|
||||
test_arithmetic_expressions(int64_t{7});
|
||||
test_modulus_expressions(int8_t{7});
|
||||
test_modulus_expressions(int16_t{7});
|
||||
test_modulus_expressions(int32_t{7});
|
||||
test_modulus_expressions(int64_t{7});
|
||||
|
||||
// unsigned integral
|
||||
test_arithmetic_expressions(uint8_t{7});
|
||||
test_arithmetic_expressions(uint16_t{7});
|
||||
test_arithmetic_expressions(uint32_t{7});
|
||||
test_arithmetic_expressions(uint64_t{7});
|
||||
test_modulus_expressions(uint8_t{7});
|
||||
test_modulus_expressions(uint16_t{7});
|
||||
test_modulus_expressions(uint32_t{7});
|
||||
test_modulus_expressions(uint64_t{7});
|
||||
|
||||
// floating point
|
||||
test_arithmetic_expressions(float{7.7});
|
||||
test_arithmetic_expressions(double{7.7});
|
||||
|
||||
// text
|
||||
test_concatenation_expressions('7');
|
||||
test_concatenation_expressions("seven");
|
||||
test_concatenation_expressions(std::string("seven"));
|
||||
test_concatenation_expressions(sqlpp::compat::string_view("seven"));
|
||||
}
|
@ -110,34 +110,6 @@ int main()
|
||||
// time_of_day
|
||||
test_result_row<std::chrono::microseconds>(std::chrono::microseconds{});
|
||||
|
||||
// Arithmetically combining optional value with non-optional value yields optional boolean.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::value(sqlpp::compat::make_optional(7)) + 8)>,
|
||||
sqlpp::compat::optional<sqlpp::numeric>>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::value(8) - sqlpp::compat::make_optional(7))>,
|
||||
sqlpp::compat::optional<sqlpp::numeric>>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::value(sqlpp::compat::make_optional(7)) / sqlpp::compat::make_optional(7))>,
|
||||
sqlpp::compat::optional<sqlpp::numeric>>::value,
|
||||
"");
|
||||
|
||||
// Same with negate.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(-sqlpp::value(sqlpp::compat::make_optional(7)))>,
|
||||
sqlpp::compat::optional<sqlpp::numeric>>::value,
|
||||
"");
|
||||
|
||||
// Arithmetically combining non-optional value with non-optional value yields optional boolean.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::value(7) + 8)>,
|
||||
sqlpp::numeric>::value,
|
||||
"");
|
||||
|
||||
// Same with negate.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(-sqlpp::value(7))>,
|
||||
sqlpp::numeric>::value,
|
||||
"");
|
||||
|
||||
// Bit shifting combining optional value with non-optional value yields optional boolean.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::value(sqlpp::compat::make_optional(7)) << 8)>,
|
||||
sqlpp::compat::optional<sqlpp::integral>>::value,
|
||||
@ -147,30 +119,6 @@ int main()
|
||||
sqlpp::compat::optional<sqlpp::integral>>::value,
|
||||
"");
|
||||
|
||||
// In expression with and without optional
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(7), 7, 8, 9))>,
|
||||
sqlpp::boolean>::value,
|
||||
"");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(7), std::vector<int>{7, 8, 9}))>,
|
||||
sqlpp::boolean>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(sqlpp::compat::make_optional(7)), 7, 8, 9))>,
|
||||
sqlpp::compat::optional<sqlpp::boolean>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(sqlpp::compat::make_optional(7)), std::vector<int>{7, 8, 9}))>,
|
||||
sqlpp::compat::optional<sqlpp::boolean>>::value,
|
||||
"");
|
||||
|
||||
// in expression
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(7), sqlpp::compat::make_optional(7), 8, 9))>,
|
||||
sqlpp::compat::optional<sqlpp::boolean>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(in(sqlpp::value(7), std::vector<sqlpp::compat::optional<int>>{7, 8, 9}))>,
|
||||
sqlpp::compat::optional<sqlpp::boolean>>::value,
|
||||
"");
|
||||
|
||||
|
||||
// assignment is no value
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(assign(bar.intN, sqlpp::compat::nullopt))>, sqlpp::no_value_t>::value, "");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(assign(bar.intN, sqlpp::default_value))>, sqlpp::no_value_t>::value, "");
|
||||
@ -180,45 +128,8 @@ int main()
|
||||
sqlpp::as(bar.intN, bar.textN);
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(bar.intN.as(bar.textN))>, sqlpp::value_type_of_t<decltype(bar.intN)>>::value, "");
|
||||
|
||||
// max can yield NULL if there are no results.
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(max(bar.intN))>, sqlpp::compat::optional<sqlpp::integral>>::value, "");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(max(foo.textNnD))>, sqlpp::compat::optional<sqlpp::text>>::value, "");
|
||||
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::max(7))>, sqlpp::compat::optional<sqlpp::integral>>::value, "");
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
|
||||
{
|
||||
// result fields are as nullable as the expressions they represent
|
||||
const auto rows = db(select(bar.id, bar.boolNn, bar.intN, seven).from(bar).unconditionally());
|
||||
auto& x = rows.front();
|
||||
#warning: test with nullable columns, too.
|
||||
#warning: test with all kinds of functions as well.
|
||||
#warning: We should actually test for the exact type!
|
||||
static_assert(not is_optional<decltype(x.id)>::value, "");
|
||||
static_assert(not is_optional<decltype(x.boolNn)>::value, "");
|
||||
static_assert(is_optional<decltype(x.intN)>::value, "");
|
||||
static_assert(not is_optional<decltype(x.s)>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
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), bar.intN.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, "");
|
||||
static_assert(is_optional<decltype(x.intN)>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
void join()
|
||||
{
|
||||
// Join
|
||||
|
Loading…
Reference in New Issue
Block a user