Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:42:38

0001 //
0002 // Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
0009 #define BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
0010 
0011 #include <boost/mysql/client_errc.hpp>
0012 #include <boost/mysql/date.hpp>
0013 #include <boost/mysql/datetime.hpp>
0014 #include <boost/mysql/diagnostics.hpp>
0015 #include <boost/mysql/error_code.hpp>
0016 #include <boost/mysql/field_kind.hpp>
0017 #include <boost/mysql/field_view.hpp>
0018 #include <boost/mysql/metadata.hpp>
0019 #include <boost/mysql/metadata_collection_view.hpp>
0020 #include <boost/mysql/string_view.hpp>
0021 #include <boost/mysql/time.hpp>
0022 
0023 #include <boost/mysql/detail/config.hpp>
0024 #include <boost/mysql/detail/typing/meta_check_context.hpp>
0025 #include <boost/mysql/detail/typing/pos_map.hpp>
0026 #include <boost/mysql/detail/void_t.hpp>
0027 
0028 #include <cstdint>
0029 #include <limits>
0030 #include <string>
0031 #include <type_traits>
0032 
0033 namespace boost {
0034 namespace mysql {
0035 namespace detail {
0036 
0037 // Helpers for integers
0038 template <class SignedInt>
0039 error_code parse_signed_int(field_view input, SignedInt& output)
0040 {
0041     using unsigned_t = typename std::make_unsigned<SignedInt>::type;
0042     using limits_t = std::numeric_limits<SignedInt>;
0043 
0044     auto kind = input.kind();
0045     if (kind == field_kind::int64)
0046     {
0047         auto v = input.get_int64();
0048         if (v < (limits_t::min)() || v > (limits_t::max)())
0049         {
0050             return client_errc::static_row_parsing_error;
0051         }
0052         output = static_cast<SignedInt>(v);
0053         return error_code();
0054     }
0055     else if (kind == field_kind::uint64)
0056     {
0057         auto v = input.get_uint64();
0058         if (v > static_cast<unsigned_t>((limits_t::max)()))
0059         {
0060             return client_errc::static_row_parsing_error;
0061         }
0062         output = static_cast<SignedInt>(v);
0063         return error_code();
0064     }
0065     else
0066     {
0067         return client_errc::static_row_parsing_error;
0068     }
0069 }
0070 
0071 template <class UnsignedInt>
0072 error_code parse_unsigned_int(field_view input, UnsignedInt& output)
0073 {
0074     if (input.kind() != field_kind::uint64)
0075     {
0076         return client_errc::static_row_parsing_error;
0077     }
0078     auto v = input.get_uint64();
0079     if (v > (std::numeric_limits<UnsignedInt>::max)())
0080     {
0081         return client_errc::static_row_parsing_error;
0082     }
0083     output = static_cast<UnsignedInt>(v);
0084     return error_code();
0085 }
0086 
0087 // We want all integer types to be allowed as fields. Some integers
0088 // may have the same width as others, but different type (e.g. long and long long
0089 // may both be 64-bit, but different types). Auxiliar int_traits to allow this to work
0090 template <class T, bool is_signed = std::is_signed<T>::value, std::size_t width = sizeof(T)>
0091 struct int_traits
0092 {
0093     static constexpr bool is_supported = false;
0094 };
0095 
0096 template <class T>
0097 struct int_traits<T, true, 1>
0098 {
0099     static constexpr bool is_supported = true;
0100     static constexpr const char* type_name = "int8_t";
0101     static bool meta_check(meta_check_context& ctx)
0102     {
0103         switch (ctx.current_meta().type())
0104         {
0105         case column_type::tinyint: return !ctx.current_meta().is_unsigned();
0106         default: return false;
0107         }
0108     }
0109     static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
0110 };
0111 
0112 template <class T>
0113 struct int_traits<T, false, 1>
0114 {
0115     static constexpr bool is_supported = true;
0116     static constexpr const char* type_name = "uint8_t";
0117     static bool meta_check(meta_check_context& ctx)
0118     {
0119         switch (ctx.current_meta().type())
0120         {
0121         case column_type::tinyint: return ctx.current_meta().is_unsigned();
0122         default: return false;
0123         }
0124     }
0125     static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
0126 };
0127 
0128 template <class T>
0129 struct int_traits<T, true, 2>
0130 {
0131     static constexpr bool is_supported = true;
0132     static constexpr const char* type_name = "int16_t";
0133     static bool meta_check(meta_check_context& ctx)
0134     {
0135         switch (ctx.current_meta().type())
0136         {
0137         case column_type::tinyint: return true;
0138         case column_type::smallint:
0139         case column_type::year: return !ctx.current_meta().is_unsigned();
0140         default: return false;
0141         }
0142     }
0143     static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
0144 };
0145 
0146 template <class T>
0147 struct int_traits<T, false, 2>
0148 {
0149     static constexpr bool is_supported = true;
0150     static constexpr const char* type_name = "uint16_t";
0151     static bool meta_check(meta_check_context& ctx)
0152     {
0153         switch (ctx.current_meta().type())
0154         {
0155         case column_type::tinyint:
0156         case column_type::smallint:
0157         case column_type::year: return ctx.current_meta().is_unsigned();
0158         default: return false;
0159         }
0160     }
0161     static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
0162 };
0163 
0164 template <class T>
0165 struct int_traits<T, true, 4>
0166 {
0167     static constexpr bool is_supported = true;
0168     static constexpr const char* type_name = "int32_t";
0169     static bool meta_check(meta_check_context& ctx)
0170     {
0171         switch (ctx.current_meta().type())
0172         {
0173         case column_type::tinyint:
0174         case column_type::smallint:
0175         case column_type::year:
0176         case column_type::mediumint: return true;
0177         case column_type::int_: return !ctx.current_meta().is_unsigned();
0178         default: return false;
0179         }
0180     }
0181     static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
0182 };
0183 
0184 template <class T>
0185 struct int_traits<T, false, 4>
0186 {
0187     static constexpr bool is_supported = true;
0188     static constexpr const char* type_name = "uint32_t";
0189     static bool meta_check(meta_check_context& ctx)
0190     {
0191         switch (ctx.current_meta().type())
0192         {
0193         case column_type::tinyint:
0194         case column_type::smallint:
0195         case column_type::year:
0196         case column_type::mediumint:
0197         case column_type::int_: return ctx.current_meta().is_unsigned();
0198         default: return false;
0199         }
0200     }
0201     static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
0202 };
0203 
0204 template <class T>
0205 struct int_traits<T, true, 8>
0206 {
0207     static constexpr bool is_supported = true;
0208     static constexpr const char* type_name = "int64_t";
0209     static bool meta_check(meta_check_context& ctx)
0210     {
0211         switch (ctx.current_meta().type())
0212         {
0213         case column_type::tinyint:
0214         case column_type::smallint:
0215         case column_type::year:
0216         case column_type::mediumint:
0217         case column_type::int_: return true;
0218         case column_type::bigint: return !ctx.current_meta().is_unsigned();
0219         default: return false;
0220         }
0221     }
0222     static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
0223 };
0224 
0225 template <class T>
0226 struct int_traits<T, false, 8>
0227 {
0228     static constexpr bool is_supported = true;
0229     static constexpr const char* type_name = "uint64_t";
0230     static bool meta_check(meta_check_context& ctx)
0231     {
0232         switch (ctx.current_meta().type())
0233         {
0234         case column_type::tinyint:
0235         case column_type::smallint:
0236         case column_type::year:
0237         case column_type::mediumint:
0238         case column_type::int_:
0239         case column_type::bigint: return ctx.current_meta().is_unsigned();
0240         case column_type::bit: return true;
0241         default: return false;
0242         }
0243     }
0244     static error_code parse(field_view input, std::uint64_t& output)
0245     {
0246         return parse_unsigned_int(input, output);
0247     }
0248 };
0249 
0250 // Traits
0251 template <typename T, class EnableIf = void>
0252 struct readable_field_traits
0253 {
0254     static constexpr bool is_supported = false;
0255 };
0256 
0257 template <>
0258 struct readable_field_traits<char, void> : int_traits<char>
0259 {
0260 };
0261 
0262 template <>
0263 struct readable_field_traits<signed char, void> : int_traits<signed char>
0264 {
0265 };
0266 
0267 template <>
0268 struct readable_field_traits<unsigned char, void> : int_traits<unsigned char>
0269 {
0270 };
0271 
0272 template <>
0273 struct readable_field_traits<short, void> : int_traits<short>
0274 {
0275 };
0276 
0277 template <>
0278 struct readable_field_traits<unsigned short, void> : int_traits<unsigned short>
0279 {
0280 };
0281 
0282 template <>
0283 struct readable_field_traits<int, void> : int_traits<int>
0284 {
0285 };
0286 
0287 template <>
0288 struct readable_field_traits<unsigned int, void> : int_traits<unsigned int>
0289 {
0290 };
0291 
0292 template <>
0293 struct readable_field_traits<long, void> : int_traits<long>
0294 {
0295 };
0296 
0297 template <>
0298 struct readable_field_traits<unsigned long, void> : int_traits<unsigned long>
0299 {
0300 };
0301 
0302 template <>
0303 struct readable_field_traits<long long, void> : int_traits<long long>
0304 {
0305 };
0306 
0307 template <>
0308 struct readable_field_traits<unsigned long long, void> : int_traits<unsigned long long>
0309 {
0310 };
0311 
0312 template <>
0313 struct readable_field_traits<bool, void>
0314 {
0315     static constexpr bool is_supported = true;
0316     static constexpr const char* type_name = "bool";
0317     static bool meta_check(meta_check_context& ctx)
0318     {
0319         return ctx.current_meta().type() == column_type::tinyint && !ctx.current_meta().is_unsigned();
0320     }
0321     static error_code parse(field_view input, bool& output)
0322     {
0323         if (input.kind() != field_kind::int64)
0324         {
0325             return client_errc::static_row_parsing_error;
0326         }
0327         output = input.get_int64() != 0;
0328         return error_code();
0329     }
0330 };
0331 
0332 template <>
0333 struct readable_field_traits<float, void>
0334 {
0335     static constexpr bool is_supported = true;
0336     static constexpr const char* type_name = "float";
0337     static bool meta_check(meta_check_context& ctx)
0338     {
0339         return ctx.current_meta().type() == column_type::float_;
0340     }
0341     static error_code parse(field_view input, float& output)
0342     {
0343         if (input.kind() != field_kind::float_)
0344         {
0345             return client_errc::static_row_parsing_error;
0346         }
0347         output = input.get_float();
0348         return error_code();
0349     }
0350 };
0351 
0352 template <>
0353 struct readable_field_traits<double, void>
0354 {
0355     static constexpr bool is_supported = true;
0356     static constexpr const char* type_name = "double";
0357     static bool meta_check(meta_check_context& ctx)
0358     {
0359         switch (ctx.current_meta().type())
0360         {
0361         case column_type::float_:
0362         case column_type::double_: return true;
0363         default: return false;
0364         }
0365     }
0366     static error_code parse(field_view input, double& output)
0367     {
0368         auto kind = input.kind();
0369         if (kind == field_kind::float_)
0370         {
0371             output = input.get_float();
0372             return error_code();
0373         }
0374         else if (kind == field_kind::double_)
0375         {
0376             output = input.get_double();
0377             return error_code();
0378         }
0379         else
0380         {
0381             return client_errc::static_row_parsing_error;
0382         }
0383     }
0384 };
0385 
0386 template <class Allocator>
0387 struct readable_field_traits<std::basic_string<char, std::char_traits<char>, Allocator>, void>
0388 {
0389     static constexpr bool is_supported = true;
0390     static constexpr const char* type_name = "string";
0391     static bool meta_check(meta_check_context& ctx)
0392     {
0393         switch (ctx.current_meta().type())
0394         {
0395         case column_type::decimal:
0396         case column_type::char_:
0397         case column_type::varchar:
0398         case column_type::text:
0399         case column_type::enum_:
0400         case column_type::set:
0401         case column_type::json: return true;
0402         default: return false;
0403         }
0404     }
0405     static error_code parse(
0406         field_view input,
0407         std::basic_string<char, std::char_traits<char>, Allocator>& output
0408     )
0409     {
0410         if (input.kind() != field_kind::string)
0411         {
0412             return client_errc::static_row_parsing_error;
0413         }
0414         output = input.get_string();
0415         return error_code();
0416     }
0417 };
0418 
0419 template <class Allocator>
0420 struct readable_field_traits<std::vector<unsigned char, Allocator>, void>
0421 {
0422     static constexpr bool is_supported = true;
0423     static constexpr const char* type_name = "blob";
0424     static bool meta_check(meta_check_context& ctx)
0425     {
0426         switch (ctx.current_meta().type())
0427         {
0428         case column_type::binary:
0429         case column_type::varbinary:
0430         case column_type::blob:
0431         case column_type::geometry:
0432         case column_type::unknown: return true;
0433         default: return false;
0434         }
0435     }
0436     static error_code parse(field_view input, std::vector<unsigned char, Allocator>& output)
0437     {
0438         if (input.kind() != field_kind::blob)
0439         {
0440             return client_errc::static_row_parsing_error;
0441         }
0442         auto view = input.get_blob();
0443         output.assign(view.begin(), view.end());
0444         return error_code();
0445     }
0446 };
0447 
0448 template <>
0449 struct readable_field_traits<date, void>
0450 {
0451     static constexpr bool is_supported = true;
0452     static constexpr const char* type_name = "date";
0453     static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::date; }
0454     static error_code parse(field_view input, date& output)
0455     {
0456         if (input.kind() != field_kind::date)
0457         {
0458             return client_errc::static_row_parsing_error;
0459         }
0460         output = input.get_date();
0461         return error_code();
0462     }
0463 };
0464 
0465 template <>
0466 struct readable_field_traits<datetime, void>
0467 {
0468     static constexpr bool is_supported = true;
0469     static constexpr const char* type_name = "datetime";
0470     static bool meta_check(meta_check_context& ctx)
0471     {
0472         switch (ctx.current_meta().type())
0473         {
0474         case column_type::datetime:
0475         case column_type::timestamp: return true;
0476         default: return false;
0477         }
0478     }
0479     static error_code parse(field_view input, datetime& output)
0480     {
0481         if (input.kind() != field_kind::datetime)
0482         {
0483             return client_errc::static_row_parsing_error;
0484         }
0485         output = input.get_datetime();
0486         return error_code();
0487     }
0488 };
0489 
0490 template <>
0491 struct readable_field_traits<time, void>
0492 {
0493     static constexpr bool is_supported = true;
0494     static constexpr const char* type_name = "time";
0495     static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::time; }
0496     static error_code parse(field_view input, time& output)
0497     {
0498         if (input.kind() != field_kind::time)
0499         {
0500             return client_errc::static_row_parsing_error;
0501         }
0502         output = input.get_time();
0503         return error_code();
0504     }
0505 };
0506 
0507 // std::optional<T> and boost::optional<T>. To avoid dependencies,
0508 // this is achieved through a "concept"
0509 template <class T, class = void>
0510 struct is_readable_optional : std::false_type
0511 {
0512 };
0513 
0514 template <class T>
0515 struct is_readable_optional<
0516     T,
0517     void_t<
0518         typename std::enable_if<
0519             std::is_same<decltype(std::declval<T&>().value()), typename T::value_type&>::value>::type,
0520         decltype(std::declval<T&>().emplace()),  // T should be default constructible
0521         decltype(std::declval<T&>().reset())>> : std::true_type
0522 {
0523 };
0524 
0525 template <class T>
0526 struct readable_field_traits<
0527     T,
0528     typename std::enable_if<
0529         is_readable_optional<T>::value && readable_field_traits<typename T::value_type>::is_supported>::type>
0530 {
0531     using value_type = typename T::value_type;
0532     static constexpr bool is_supported = true;
0533     static constexpr const char* type_name = readable_field_traits<value_type>::type_name;
0534     static bool meta_check(meta_check_context& ctx)
0535     {
0536         ctx.set_nullability_checked();
0537         return readable_field_traits<value_type>::meta_check(ctx);
0538     }
0539     static error_code parse(field_view input, T& output)
0540     {
0541         if (input.is_null())
0542         {
0543             output.reset();
0544             return error_code();
0545         }
0546         else
0547         {
0548             output.emplace();
0549             return readable_field_traits<value_type>::parse(input, output.value());
0550         }
0551     }
0552 };
0553 
0554 template <class T>
0555 struct is_readable_field
0556 {
0557     static constexpr bool value = readable_field_traits<T>::is_supported;
0558 };
0559 
0560 template <typename ReadableField>
0561 void meta_check_field_impl(meta_check_context& ctx)
0562 {
0563     using traits_t = readable_field_traits<ReadableField>;
0564 
0565     // Verify that the field is present
0566     if (ctx.is_current_field_absent())
0567     {
0568         ctx.add_field_absent_error();
0569         return;
0570     }
0571 
0572     // Perform the check
0573     bool ok = traits_t::meta_check(ctx);
0574     if (!ok)
0575     {
0576         ctx.add_type_mismatch_error(traits_t::type_name);
0577     }
0578 
0579     // Check nullability
0580     if (!ctx.nullability_checked() && !ctx.current_meta().is_not_null())
0581     {
0582         ctx.add_nullability_error();
0583     }
0584 }
0585 
0586 template <typename ReadableField>
0587 void meta_check_field(meta_check_context& ctx)
0588 {
0589     static_assert(is_readable_field<ReadableField>::value, "Should be a ReadableField");
0590     meta_check_field_impl<ReadableField>(ctx);
0591     ctx.advance();
0592 }
0593 
0594 struct meta_check_field_fn
0595 {
0596     meta_check_context ctx;
0597 
0598     template <class T>
0599     void operator()(T)
0600     {
0601         meta_check_field<typename T::type>(ctx);
0602     }
0603 };
0604 
0605 template <typename ReadableFieldList>
0606 error_code meta_check_field_type_list(
0607     span<const std::size_t> field_map,
0608     name_table_t name_table,
0609     metadata_collection_view meta,
0610     diagnostics& diag
0611 )
0612 {
0613     meta_check_field_fn fn{meta_check_context(field_map, name_table, meta)};
0614     boost::mp11::mp_for_each<ReadableFieldList>(fn);
0615     return fn.ctx.check_errors(diag);
0616 }
0617 
0618 }  // namespace detail
0619 }  // namespace mysql
0620 }  // namespace boost
0621 
0622 #endif