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

Add basic serialize functions and start documenting differences

This commit is contained in:
Roland Bock 2024-07-13 13:39:36 +02:00
parent 2b7e0ee6a5
commit c2c5f15690
15 changed files with 277 additions and 151 deletions

View File

@ -0,0 +1,39 @@
# New data types
## optional
Nullable values are represented as `std::optional` if you are using C++17 or later, `sqlpp::compat::optional` otherwise.
## string_view
Text results are represented as `std::string_view` if you are using C++17 or later, `sqlpp::compat::string_view` otherwise.
# span<uint8_t>
Blob results are represented as `std::span<uint8_t>` if you are using C++20 or later, `sqlpp::compat::span<uint8_t>` otherwise.
# Result values
Result rows are represented as structs with the respective columns represented as data members. In version 1.0, these data members used to have a type that wrapped the actual data and provided conversion operators and functions to check for NULL.
Now data members have the correct data type, e.g. `int64_t` or `optional<string_view>`.
# No read-only columns
Version 1.0 had the concept of read-only columns, e.g. you could not modify a column with auto-increment values.
This concept has been removed. In most cases, you will still not want to modify columns with auto-increment values, but you can do it now, if you want to.
# IS DISCTINCT FROM
Version 1.0 used to have `is_equal_to_or_null` which translated to either `=` or `IS NULL`. While useful, this did not work with parameters.
The library now offers `is_distinct_from` and `is_not_distinct_from` which safely compares with actual values and `NULL`.
# Selecting aggregate functions
In version 1.0, many functions automatically came with a name. This was convenient for selecting, but also rather unspecific, e.g. `select(max(tab.price)).from(tab).unconditionally()` would yield rows with data member called `count_`.
Now, functions do not come with names. While this will lead to lightly more code, the assumption is that this will lead to slightly more readable code, e.g.
```
SQLPP_ALIAS_PROVIDER(max_price);
// ...
for (const auto& row : db(select(max(tab.price)).from(tab).unconditionally()))
{
std::cout << row.max_price << '\n';
}
```

View File

@ -112,6 +112,11 @@ namespace sqlpp
using type = typename ColumnSpec::value_type;
};
template<typename Table, typename ColumnSpec>
struct has_default<column_t<Table, ColumnSpec>> : public ColumnSpec::has_default
{
};
template <typename Context, typename Table, typename ColumnSpec>
Context& serialize(const column_t<Table, ColumnSpec>&, Context& context)
{

View File

@ -47,7 +47,6 @@
#include <sqlpp11/verbatim_table.h>
#include <sqlpp11/value.h>
#include <sqlpp11/value_or_null.h>
#include <sqlpp11/is_equal_to_or_null.h>
#include <sqlpp11/eval.h>
namespace sqlpp

View File

@ -1,76 +0,0 @@
#pragma once
/*
* Copyright (c) 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 <sqlpp11/value_or_null.h>
namespace sqlpp
{
template <typename Expr, typename ValueType>
struct is_equal_to_or_null_t: public expression_operators<is_equal_to_or_null_t<Expr, ValueType>, boolean>,
public alias_operators<is_equal_to_or_null_t<Expr, ValueType>>
{
using _traits = make_traits<boolean, tag::is_expression>;
using _nodes = detail::type_vector<Expr, value_or_null_t<ValueType>>;
is_equal_to_or_null_t(Expr expr, value_or_null_t<ValueType> value) : _expr(expr), _value(value)
{
}
is_equal_to_or_null_t(const is_equal_to_or_null_t&) = default;
is_equal_to_or_null_t(is_equal_to_or_null_t&&) = default;
is_equal_to_or_null_t& operator=(const is_equal_to_or_null_t&) = default;
is_equal_to_or_null_t& operator=(is_equal_to_or_null_t&&) = default;
~is_equal_to_or_null_t() = default;
Expr _expr;
value_or_null_t<ValueType> _value;
};
template <typename Context, typename Expr, typename ValueType>
Context& serialize(const is_equal_to_or_null_t<Expr, ValueType>& t, Context& context)
{
if (t._value._is_null)
serialize(t._expr.is_null(), context);
else
serialize(t._expr == t._value, context);
return context;
}
template <typename Expr, typename ValueType>
auto is_equal_to_or_null(Expr expr, value_or_null_t<ValueType> value) -> is_equal_to_or_null_t<Expr, ValueType>
{
static_assert(is_expression_t<Expr>::value,
"is_equal_to_or_null() is to be called an expression (e.g. a column) and a value_or_null expression");
static_assert(std::is_same<value_type_of_t<Expr>, ValueType>::value,
"is_equal_to_or_null() arguments need to have the same value type");
return {expr, value};
}
} // namespace sqlpp

View File

@ -177,7 +177,7 @@ namespace sqlpp
};
template <typename L, typename R>
using check_modulus_args = std::enable_if_t<has_integral_value<L>::value and has_integral_value<R>::value>;
using check_modulus_args = std::enable_if_t<(is_integral<L>::value or is_unsigned_integral<L>::value) and (is_integral<R>::value or is_unsigned_integral<R>::value)>;
template <typename L, typename R, typename = check_modulus_args<L, R>>
constexpr auto operator%(L l, R r) -> arithmetic_expression<L, modulus, R>

View File

@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <utility>
#include <sqlpp11/column_fwd.h>
#include <sqlpp11/type_traits.h>
#include <sqlpp11/default_value.h>
@ -40,16 +41,21 @@ namespace sqlpp
R value;
};
#warning rename is_column, is_default, values_are_comparable -> values_are_compatible
#warning document the change that there is no read-only!
#warning handle default_t, but only if the column does have a default!
template <typename L, typename R>
using check_assign_args = std::enable_if_t<is_column_t<L>::value and values_are_comparable<L, R>::value and
(can_be_null<L>::value or not is_optional<R>::value) and
(has_default_t<L>::value or not std::is_same<R, default_value_t>::value)>;
using check_assign_args =
std::enable_if_t<values_are_comparable<L, R>::value and (can_be_null<L>::value or not is_optional<R>::value)>;
template <typename L, typename R, typename = check_assign_args<L, R>>
constexpr auto assign(L column, R value) -> assign_expression<L, R>
template <typename L>
using check_assign_default_args = std::enable_if_t<has_default<L>::value>;
template <typename Table, typename ColumnSpec, typename R, typename = check_assign_args<column_t<Table, ColumnSpec>, R>>
constexpr auto assign(column_t<Table, ColumnSpec> column, R value) -> assign_expression<column_t<Table, ColumnSpec>, R>
{
return {std::move(column), std::move(value)};
}
template <typename Table, typename ColumnSpec, typename = check_assign_default_args<column_t<Table, ColumnSpec>>>
constexpr auto assign(column_t<Table, ColumnSpec> column, default_value_t value) -> assign_expression<column_t<Table, ColumnSpec>, default_value_t>
{
return {std::move(column), std::move(value)};
}
@ -79,4 +85,13 @@ namespace sqlpp
return to_sql_string(context, t.column) + " = " + to_sql_string(context, embrace(t.value));
}
*/
template <typename Context, typename L, typename R>
Context& serialize(const assign_expression<L, R>& t, Context& context)
{
serialize(simple_column(t._lhs), context);
context << "=";
serialize_operand(t._rhs, context);
return context;
}
} // namespace sqlpp

View File

@ -82,7 +82,7 @@ namespace sqlpp
};
template <typename L, typename R>
using check_bit_expression_args = std::enable_if_t<has_integral_value<L>::value and has_integral_value<R>::value>;
using check_bit_expression_args = std::enable_if_t<is_integral<L>::value and (is_integral<R>::value or is_unsigned_integral<R>::value)>;
#if 0
template <typename L, typename Operator, typename R>

View File

@ -45,7 +45,6 @@ namespace sqlpp
using type = boolean;
};
#warning: Document that functions dont come with their default alias any more
template <typename SubSelect, typename = check_exists_arg<SubSelect>>
constexpr auto exists(SubSelect sub_select) -> exists_t<SubSelect>
{

View File

@ -61,7 +61,7 @@ namespace sqlpp
};
template <typename L, typename R>
using check_logical_args = std::enable_if_t<has_boolean_value<L>::value and has_boolean_value<R>::value>;
using check_logical_args = std::enable_if_t<is_boolean<L>::value and is_boolean<R>::value>;
template <typename L, typename Operator, typename R>
struct value_type_of<logical_expression<L, Operator, R>>

View File

@ -46,4 +46,164 @@ namespace sqlpp
return context;
}
template <typename Context>
auto serialize(const bool& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const int8_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const int16_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const int32_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const int64_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const uint8_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const uint16_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const uint32_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const uint64_t& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const float& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const double& t, Context& context) -> Context&
{
#warning: Analyze precision. There was a bug about this...
context << t;
return context;
}
template <typename Context>
auto serialize(const char& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const char* t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const std::string& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
auto serialize(const sqlpp::compat::string_view& t, Context& context) -> Context&
{
context << t;
return context;
}
template <typename Context>
Context& serialize(const std::vector<uint8_t>& t, Context& context)
{
constexpr char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
context << "x'";
for (const auto c : t)
{
context << hexChars[c >> 4] << hexChars[c & 0x0F];
}
context << '\'';
return context;
}
template <typename Context>
Context& serialize(const ::sqlpp::chrono::day_point& t, Context& context)
{
const auto ymd = ::date::year_month_day{t};
context << "DATE '" << ymd << "'";
return context;
}
template <typename Context>
Context& serialize(const std::chrono::microseconds& t, Context& context)
{
context << '\'' << ::date::make_time(t) << '\'';
return context;
}
template <typename Period, typename Context>
Context& serialize(const std::chrono::time_point<std::chrono::system_clock, Period>& t, Context& context)
{
const auto dp = ::sqlpp::chrono::floor<::date::days>(t);
const auto time = ::date::make_time(t - dp);
const auto ymd = ::date::year_month_day{dp};
context << "TIMESTAMP '" << ymd << ' ' << time << "'";
return context;
}
template <typename T, typename Context>
auto serialize(const sqlpp::compat::optional<T>& t, Context& context) -> Context&
{
if (not t.has_value())
{
context << "NULL";
}
else
{
serialize(*t, context);
}
return context;
}
} // namespace sqlpp

View File

@ -115,7 +115,6 @@ namespace sqlpp
struct no_value_t;
// This requires specializations for anything that has a value, like a column or a boolean expression
template <typename T>
struct value_type_of
{
@ -134,6 +133,11 @@ namespace sqlpp
template <typename T>
struct has_value_type : public std::integral_constant<bool, not std::is_same<value_type_of_t<T>, no_value_t>::value> {};
template <typename T>
struct has_default : public std::false_type
{
};
template <typename T>
struct is_not_cpp_bool_t
{
@ -181,7 +185,6 @@ namespace sqlpp
template <>
struct value_type_of<sqlpp::compat::string_view> { using type = text; };
/////////////////
struct blob;
template <>
struct value_type_of<std::vector<std::uint8_t>> { using type = blob; };
@ -200,63 +203,39 @@ namespace sqlpp
struct time_point;
template <typename Period>
struct value_type_of<std::chrono::time_point<std::chrono::system_clock, Period>> { using type = time_point; };
/////////////////
template <typename T>
struct is_boolean : public std::is_same<T, bool>
struct is_boolean : public std::is_same<remove_optional_t<value_type_of_t<T>>, boolean>
{
};
template <>
struct is_boolean<boolean> : public std::true_type {
};
template <>
struct is_boolean<sqlpp::compat::nullopt_t> : public std::true_type {
};
template <typename T>
struct has_boolean_value : public std::integral_constant<bool,
is_boolean<remove_optional_t<T>>::value or
is_boolean<remove_optional_t<value_type_of_t<T>>>::value>
{
};
struct integral;
template <typename T>
struct is_integral : public std::is_integral<T>{};
template <>
struct is_integral<char> : public std::false_type // char is text
struct is_integral : public std::is_same<remove_optional_t<value_type_of_t<T>>, integral>
{
};
template <>
struct is_integral<bool> : public std::false_type // bool is boolean
{
struct is_integral<sqlpp::compat::nullopt_t> : public std::true_type {
};
template <>
struct is_integral<sqlpp::compat::nullopt_t> : public std::true_type{};
template <>
struct is_integral<integral> : public std::true_type{};
template <typename T>
struct has_integral_value
: public std::integral_constant<bool,
is_integral<remove_optional_t<T>>::value or
is_integral<remove_optional_t<value_type_of_t<T>>>::value>
struct is_unsigned_integral : public std::is_same<remove_optional_t<value_type_of_t<T>>, unsigned_integral>
{
};
template <>
struct is_unsigned_integral<sqlpp::compat::nullopt_t> : public std::true_type {
};
// A generic numeric type which could be (unsigned) integral or floating point.
struct numeric;
template <typename T>
struct is_numeric : public std::integral_constant<bool, is_integral<T>::value or std::is_floating_point<T>::value>{};
struct is_numeric : public std::integral_constant<bool, is_integral<T>::value or is_unsigned_integral<T>::value or std::is_floating_point<T>::value>{};
template <>
struct is_numeric<numeric> : public std::true_type{};
@ -375,7 +354,7 @@ namespace sqlpp
struct values_are_comparable
: public std::integral_constant<bool,
(has_blob_value<L>::value and has_blob_value<R>::value) or
(has_boolean_value<L>::value and has_boolean_value<R>::value) or
(is_boolean<L>::value and is_boolean<R>::value) or
(has_day_point_value<L>::value and has_day_point_value<R>::value) or
(has_numeric_value<L>::value and has_numeric_value<R>::value) or
(has_text_value<L>::value and has_text_value<R>::value) or
@ -421,6 +400,7 @@ namespace sqlpp
using is_time_point_t = std::is_same<value_type_of_t<T>, time_point>;
// joined data type
#warning: These something_t data type classifiers should be removed
template <typename T>
using is_numeric_t =
logic::any_t<is_integral_t<T>::value, is_unsigned_integral_t<T>::value, is_floating_point_t<T>::value>;
@ -469,9 +449,6 @@ namespace sqlpp
SQLPP_VALUE_TRAIT_GENERATOR(is_union_flag)
SQLPP_VALUE_TRAIT_GENERATOR(is_result_field)
#warning Document loss of must_not_insert/update and require_insert (and new has_default)
SQLPP_VALUE_TRAIT_GENERATOR(has_default)
SQLPP_VALUE_TRAIT_GENERATOR(is_statement)
SQLPP_VALUE_TRAIT_GENERATOR(is_prepared_statement)
SQLPP_VALUE_TRAIT_GENERATOR(is_union)

View File

@ -43,6 +43,7 @@ namespace sqlpp
T _value;
};
template<typename T>
struct value_type_of<value_t<T>>
{
@ -52,9 +53,19 @@ namespace sqlpp
template <typename T>
using check_value_arg = std::enable_if_t<not std::is_same<value_type_of_t<T>, no_value_t>::value and values_are_comparable<T, T>::value>;
template <typename Context, typename T>
Context& serialize(const value_t<T>& t, Context& context)
{
#warning: Untested
serialize(t._value, context);
return context;
}
template <typename T, typename = check_value_arg<T>>
auto value(T t) -> value_t<T>
{
return {std::move(t)};
}
} // namespace sqlpp

View File

@ -743,9 +743,9 @@ def createHeader():
column.hasAutoValue or \
(autoId and sqlColumnName == "id")
if columnHasDefault:
print(" using has_default_value = std::true_type;", file=header)
print(" using has_default = std::true_type;", file=header)
else:
print(" using has_default_value = std::false_type;", file=header)
print(" using has_default = std::false_type;", file=header)
print(" };", file=header)
print(" } // namespace " + tableNamespace, file=header)
print("", file=header)

View File

@ -106,8 +106,9 @@ int main()
static_assert(is_same_type<decltype(row.b), sqlpp::compat::optional<int64_t>>(), "");
}
static_assert(sqlpp::has_boolean_value<bool>::value, "");
static_assert(sqlpp::has_boolean_value<decltype(bar.boolNn)>::value, "");
#warning: also test with optional
static_assert(sqlpp::is_boolean<bool>::value, "");
static_assert(sqlpp::is_boolean<decltype(bar.boolNn)>::value, "");
static_assert(sqlpp::has_text_value<decltype(bar.textN)>::value, "");
static_assert(sqlpp::has_text_value<std::string>::value, "");
static_assert(sqlpp::has_numeric_value<int>::value, "");
@ -257,8 +258,6 @@ int main()
sqlpp::numeric>::value,
"");
#warning: Is bit-shifting with NULL "legal"? probably.
#warning: Note that bit-shifting is defined for signed int only
// 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,
@ -293,7 +292,7 @@ int main()
// 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, "");
#warning: This is not the real thing yet
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(assign(bar.intN, sqlpp::default_value))>, sqlpp::no_value_t>::value, "");
// as expressions retain the value type of the real thing
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, "");
@ -304,8 +303,6 @@ int main()
static_assert(std::is_same<sqlpp::value_type_of_t<decltype(sqlpp::max(7))>, sqlpp::compat::optional<sqlpp::integral>>::value, "");
#warning: No magic for NULL in operators, e.g. comparison. It might therefore be reasonable to disallow comparison with optoinal values? But then again, columns can also be NULL, failing to compare to anything. In any case, do not translate `a == nullopt` to `a IS NULL`. Same for parameters.
#if 0

View File

@ -25,7 +25,7 @@ namespace test
};
};
using value_type = ::sqlpp::integer;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct TextNnD
{
@ -42,7 +42,7 @@ namespace test
};
};
using value_type = ::sqlpp::text;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct IntN
{
@ -59,7 +59,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::integer>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct DoubleN
{
@ -76,7 +76,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::floating_point>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct UIntN
{
@ -93,7 +93,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::integer_unsigned>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct BlobN
{
@ -110,7 +110,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::blob>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
} // namespace TabFoo_
@ -152,7 +152,7 @@ namespace test
};
};
using value_type = ::sqlpp::integer;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct TextN
{
@ -169,7 +169,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::text>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct BoolNn
{
@ -186,7 +186,7 @@ namespace test
};
};
using value_type = ::sqlpp::boolean;
using has_default_value = std::false_type;
using has_default = std::false_type;
};
struct IntN
{
@ -203,7 +203,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::integer>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
} // namespace TabBar_
@ -243,7 +243,7 @@ namespace test
};
};
using value_type = ::sqlpp::integer;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct DayPointN
{
@ -260,7 +260,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::day_point>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct TimePointN
{
@ -277,7 +277,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::time_point>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
struct TimeOfDayN
{
@ -294,7 +294,7 @@ namespace test
};
};
using value_type = ::sqlpp::compat::optional<::sqlpp::time_of_day>;
using has_default_value = std::true_type;
using has_default = std::true_type;
};
} // namespace TabDateTime_