// // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace mysql { namespace detail { // Helpers for integers template error_code parse_signed_int(field_view input, SignedInt& output) { using unsigned_t = typename std::make_unsigned::type; using limits_t = std::numeric_limits; 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(v); return error_code(); } else if (kind == field_kind::uint64) { auto v = input.get_uint64(); if (v > static_cast((limits_t::max)())) { return client_errc::static_row_parsing_error; } output = static_cast(v); return error_code(); } else { return client_errc::static_row_parsing_error; } } template 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::max)()) { return client_errc::static_row_parsing_error; } output = static_cast(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 ::value, std::size_t width = sizeof(T)> struct int_traits { static constexpr bool is_supported = false; }; template struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct int_traits { 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 struct readable_field_traits { static constexpr bool is_supported = false; }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits : int_traits { }; template <> struct readable_field_traits { 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 { 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 { 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 struct readable_field_traits, 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, Allocator>& output ) { if (input.kind() != field_kind::string) { return client_errc::static_row_parsing_error; } output = input.get_string(); return error_code(); } }; template struct readable_field_traits, 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& 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 { 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 { 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 { 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 and boost::optional. To avoid dependencies, // this is achieved through a "concept" template struct is_readable_optional : std::false_type { }; template struct is_readable_optional< T, void_t< typename std::enable_if< std::is_same().value()), typename T::value_type&>::value>::type, decltype(std::declval().emplace()), // T should be default constructible decltype(std::declval().reset())>> : std::true_type { }; template struct readable_field_traits< T, typename std::enable_if< is_readable_optional::value && readable_field_traits::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::type_name; static bool meta_check(meta_check_context& ctx) { ctx.set_nullability_checked(); return readable_field_traits::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::parse(input, output.value()); } } }; template struct is_readable_field { static constexpr bool value = readable_field_traits::is_supported; }; template void meta_check_field_impl(meta_check_context& ctx) { using traits_t = readable_field_traits; // 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 void meta_check_field(meta_check_context& ctx) { static_assert(is_readable_field::value, "Should be a ReadableField"); meta_check_field_impl(ctx); ctx.advance(); } } // namespace detail } // namespace mysql } // namespace boost #endif