123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- //
- // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- #ifndef BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
- #define BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
- #include <boost/mysql/client_errc.hpp>
- #include <boost/mysql/date.hpp>
- #include <boost/mysql/datetime.hpp>
- #include <boost/mysql/diagnostics.hpp>
- #include <boost/mysql/error_code.hpp>
- #include <boost/mysql/field_kind.hpp>
- #include <boost/mysql/field_view.hpp>
- #include <boost/mysql/metadata.hpp>
- #include <boost/mysql/metadata_collection_view.hpp>
- #include <boost/mysql/string_view.hpp>
- #include <boost/mysql/time.hpp>
- #include <boost/mysql/detail/config.hpp>
- #include <boost/mysql/detail/typing/meta_check_context.hpp>
- #include <boost/mysql/detail/typing/pos_map.hpp>
- #include <boost/mysql/detail/void_t.hpp>
- #include <boost/mp11/algorithm.hpp>
- #include <boost/mp11/utility.hpp>
- #include <cstdint>
- #include <limits>
- #include <string>
- #include <type_traits>
- namespace boost {
- namespace mysql {
- namespace detail {
- // Helpers for integers
- template <class SignedInt>
- error_code parse_signed_int(field_view input, SignedInt& output)
- {
- using unsigned_t = typename std::make_unsigned<SignedInt>::type;
- using limits_t = std::numeric_limits<SignedInt>;
- auto kind = input.kind();
- if (kind == field_kind::int64)
- {
- auto v = input.get_int64();
- if (v < (limits_t::min)() || v > (limits_t::max)())
- {
- return client_errc::static_row_parsing_error;
- }
- output = static_cast<SignedInt>(v);
- return error_code();
- }
- else if (kind == field_kind::uint64)
- {
- auto v = input.get_uint64();
- if (v > static_cast<unsigned_t>((limits_t::max)()))
- {
- return client_errc::static_row_parsing_error;
- }
- output = static_cast<SignedInt>(v);
- return error_code();
- }
- else
- {
- return client_errc::static_row_parsing_error;
- }
- }
- template <class UnsignedInt>
- error_code parse_unsigned_int(field_view input, UnsignedInt& output)
- {
- if (input.kind() != field_kind::uint64)
- {
- return client_errc::static_row_parsing_error;
- }
- auto v = input.get_uint64();
- if (v > (std::numeric_limits<UnsignedInt>::max)())
- {
- return client_errc::static_row_parsing_error;
- }
- output = static_cast<UnsignedInt>(v);
- return error_code();
- }
- // We want all integer types to be allowed as fields. Some integers
- // may have the same width as others, but different type (e.g. long and long long
- // may both be 64-bit, but different types). Auxiliar int_traits to allow this to work
- template <class T, bool is_signed = std::is_signed<T>::value, std::size_t width = sizeof(T)>
- struct int_traits
- {
- static constexpr bool is_supported = false;
- };
- template <class T>
- struct int_traits<T, true, 1>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "int8_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint: return !ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
- };
- template <class T>
- struct int_traits<T, false, 1>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "uint8_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint: return ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
- };
- template <class T>
- struct int_traits<T, true, 2>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "int16_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint: return true;
- case column_type::smallint:
- case column_type::year: return !ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
- };
- template <class T>
- struct int_traits<T, false, 2>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "uint16_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint:
- case column_type::smallint:
- case column_type::year: return ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
- };
- template <class T>
- struct int_traits<T, true, 4>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "int32_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint:
- case column_type::smallint:
- case column_type::year:
- case column_type::mediumint: return true;
- case column_type::int_: return !ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
- };
- template <class T>
- struct int_traits<T, false, 4>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "uint32_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint:
- case column_type::smallint:
- case column_type::year:
- case column_type::mediumint:
- case column_type::int_: return ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
- };
- template <class T>
- struct int_traits<T, true, 8>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "int64_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint:
- case column_type::smallint:
- case column_type::year:
- case column_type::mediumint:
- case column_type::int_: return true;
- case column_type::bigint: return !ctx.current_meta().is_unsigned();
- default: return false;
- }
- }
- static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
- };
- template <class T>
- struct int_traits<T, false, 8>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "uint64_t";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::tinyint:
- case column_type::smallint:
- case column_type::year:
- case column_type::mediumint:
- case column_type::int_:
- case column_type::bigint: return ctx.current_meta().is_unsigned();
- case column_type::bit: return true;
- default: return false;
- }
- }
- static error_code parse(field_view input, std::uint64_t& output)
- {
- return parse_unsigned_int(input, output);
- }
- };
- // Traits
- template <typename T, class EnableIf = void>
- struct readable_field_traits
- {
- static constexpr bool is_supported = false;
- };
- template <>
- struct readable_field_traits<char, void> : int_traits<char>
- {
- };
- template <>
- struct readable_field_traits<signed char, void> : int_traits<signed char>
- {
- };
- template <>
- struct readable_field_traits<unsigned char, void> : int_traits<unsigned char>
- {
- };
- template <>
- struct readable_field_traits<short, void> : int_traits<short>
- {
- };
- template <>
- struct readable_field_traits<unsigned short, void> : int_traits<unsigned short>
- {
- };
- template <>
- struct readable_field_traits<int, void> : int_traits<int>
- {
- };
- template <>
- struct readable_field_traits<unsigned int, void> : int_traits<unsigned int>
- {
- };
- template <>
- struct readable_field_traits<long, void> : int_traits<long>
- {
- };
- template <>
- struct readable_field_traits<unsigned long, void> : int_traits<unsigned long>
- {
- };
- template <>
- struct readable_field_traits<long long, void> : int_traits<long long>
- {
- };
- template <>
- struct readable_field_traits<unsigned long long, void> : int_traits<unsigned long long>
- {
- };
- template <>
- struct readable_field_traits<bool, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "bool";
- static bool meta_check(meta_check_context& ctx)
- {
- return ctx.current_meta().type() == column_type::tinyint && !ctx.current_meta().is_unsigned();
- }
- static error_code parse(field_view input, bool& output)
- {
- if (input.kind() != field_kind::int64)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_int64() != 0;
- return error_code();
- }
- };
- template <>
- struct readable_field_traits<float, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "float";
- static bool meta_check(meta_check_context& ctx)
- {
- return ctx.current_meta().type() == column_type::float_;
- }
- static error_code parse(field_view input, float& output)
- {
- if (input.kind() != field_kind::float_)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_float();
- return error_code();
- }
- };
- template <>
- struct readable_field_traits<double, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "double";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::float_:
- case column_type::double_: return true;
- default: return false;
- }
- }
- static error_code parse(field_view input, double& output)
- {
- auto kind = input.kind();
- if (kind == field_kind::float_)
- {
- output = input.get_float();
- return error_code();
- }
- else if (kind == field_kind::double_)
- {
- output = input.get_double();
- return error_code();
- }
- else
- {
- return client_errc::static_row_parsing_error;
- }
- }
- };
- template <class Allocator>
- struct readable_field_traits<std::basic_string<char, std::char_traits<char>, Allocator>, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "string";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::decimal:
- case column_type::char_:
- case column_type::varchar:
- case column_type::text:
- case column_type::enum_:
- case column_type::set:
- case column_type::json: return true;
- default: return false;
- }
- }
- static error_code parse(
- field_view input,
- std::basic_string<char, std::char_traits<char>, Allocator>& output
- )
- {
- if (input.kind() != field_kind::string)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_string();
- return error_code();
- }
- };
- template <class Allocator>
- struct readable_field_traits<std::vector<unsigned char, Allocator>, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "blob";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::binary:
- case column_type::varbinary:
- case column_type::blob:
- case column_type::geometry:
- case column_type::unknown: return true;
- default: return false;
- }
- }
- static error_code parse(field_view input, std::vector<unsigned char, Allocator>& output)
- {
- if (input.kind() != field_kind::blob)
- {
- return client_errc::static_row_parsing_error;
- }
- auto view = input.get_blob();
- output.assign(view.begin(), view.end());
- return error_code();
- }
- };
- template <>
- struct readable_field_traits<date, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "date";
- static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::date; }
- static error_code parse(field_view input, date& output)
- {
- if (input.kind() != field_kind::date)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_date();
- return error_code();
- }
- };
- template <>
- struct readable_field_traits<datetime, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "datetime";
- static bool meta_check(meta_check_context& ctx)
- {
- switch (ctx.current_meta().type())
- {
- case column_type::datetime:
- case column_type::timestamp: return true;
- default: return false;
- }
- }
- static error_code parse(field_view input, datetime& output)
- {
- if (input.kind() != field_kind::datetime)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_datetime();
- return error_code();
- }
- };
- template <>
- struct readable_field_traits<time, void>
- {
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = "time";
- static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::time; }
- static error_code parse(field_view input, time& output)
- {
- if (input.kind() != field_kind::time)
- {
- return client_errc::static_row_parsing_error;
- }
- output = input.get_time();
- return error_code();
- }
- };
- // std::optional<T> and boost::optional<T>. To avoid dependencies,
- // this is achieved through a "concept"
- template <class T, class = void>
- struct is_readable_optional : std::false_type
- {
- };
- template <class T>
- struct is_readable_optional<
- T,
- void_t<
- typename std::enable_if<
- std::is_same<decltype(std::declval<T&>().value()), typename T::value_type&>::value>::type,
- decltype(std::declval<T&>().emplace()), // T should be default constructible
- decltype(std::declval<T&>().reset())>> : std::true_type
- {
- };
- template <class T>
- struct readable_field_traits<
- T,
- typename std::enable_if<
- is_readable_optional<T>::value && readable_field_traits<typename T::value_type>::is_supported>::type>
- {
- using value_type = typename T::value_type;
- static constexpr bool is_supported = true;
- static BOOST_INLINE_CONSTEXPR const char* type_name = readable_field_traits<value_type>::type_name;
- static bool meta_check(meta_check_context& ctx)
- {
- ctx.set_nullability_checked();
- return readable_field_traits<value_type>::meta_check(ctx);
- }
- static error_code parse(field_view input, T& output)
- {
- if (input.is_null())
- {
- output.reset();
- return error_code();
- }
- else
- {
- output.emplace();
- return readable_field_traits<value_type>::parse(input, output.value());
- }
- }
- };
- template <class T>
- struct is_readable_field
- {
- static constexpr bool value = readable_field_traits<T>::is_supported;
- };
- template <typename ReadableField>
- void meta_check_field_impl(meta_check_context& ctx)
- {
- using traits_t = readable_field_traits<ReadableField>;
- // Verify that the field is present
- if (ctx.is_current_field_absent())
- {
- ctx.add_field_absent_error();
- return;
- }
- // Perform the check
- bool ok = traits_t::meta_check(ctx);
- if (!ok)
- {
- ctx.add_type_mismatch_error(traits_t::type_name);
- }
- // Check nullability
- if (!ctx.nullability_checked() && !ctx.current_meta().is_not_null())
- {
- ctx.add_nullability_error();
- }
- }
- template <typename ReadableField>
- void meta_check_field(meta_check_context& ctx)
- {
- static_assert(is_readable_field<ReadableField>::value, "Should be a ReadableField");
- meta_check_field_impl<ReadableField>(ctx);
- ctx.advance();
- }
- } // namespace detail
- } // namespace mysql
- } // namespace boost
- #endif
|