readable_field_traits.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. //
  2. // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
  8. #define BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
  9. #include <boost/mysql/client_errc.hpp>
  10. #include <boost/mysql/date.hpp>
  11. #include <boost/mysql/datetime.hpp>
  12. #include <boost/mysql/diagnostics.hpp>
  13. #include <boost/mysql/error_code.hpp>
  14. #include <boost/mysql/field_kind.hpp>
  15. #include <boost/mysql/field_view.hpp>
  16. #include <boost/mysql/metadata.hpp>
  17. #include <boost/mysql/metadata_collection_view.hpp>
  18. #include <boost/mysql/string_view.hpp>
  19. #include <boost/mysql/time.hpp>
  20. #include <boost/mysql/detail/config.hpp>
  21. #include <boost/mysql/detail/typing/meta_check_context.hpp>
  22. #include <boost/mysql/detail/typing/pos_map.hpp>
  23. #include <boost/mysql/detail/void_t.hpp>
  24. #include <boost/mp11/algorithm.hpp>
  25. #include <boost/mp11/utility.hpp>
  26. #include <cstdint>
  27. #include <limits>
  28. #include <string>
  29. #include <type_traits>
  30. namespace boost {
  31. namespace mysql {
  32. namespace detail {
  33. // Helpers for integers
  34. template <class SignedInt>
  35. error_code parse_signed_int(field_view input, SignedInt& output)
  36. {
  37. using unsigned_t = typename std::make_unsigned<SignedInt>::type;
  38. using limits_t = std::numeric_limits<SignedInt>;
  39. auto kind = input.kind();
  40. if (kind == field_kind::int64)
  41. {
  42. auto v = input.get_int64();
  43. if (v < (limits_t::min)() || v > (limits_t::max)())
  44. {
  45. return client_errc::static_row_parsing_error;
  46. }
  47. output = static_cast<SignedInt>(v);
  48. return error_code();
  49. }
  50. else if (kind == field_kind::uint64)
  51. {
  52. auto v = input.get_uint64();
  53. if (v > static_cast<unsigned_t>((limits_t::max)()))
  54. {
  55. return client_errc::static_row_parsing_error;
  56. }
  57. output = static_cast<SignedInt>(v);
  58. return error_code();
  59. }
  60. else
  61. {
  62. return client_errc::static_row_parsing_error;
  63. }
  64. }
  65. template <class UnsignedInt>
  66. error_code parse_unsigned_int(field_view input, UnsignedInt& output)
  67. {
  68. if (input.kind() != field_kind::uint64)
  69. {
  70. return client_errc::static_row_parsing_error;
  71. }
  72. auto v = input.get_uint64();
  73. if (v > (std::numeric_limits<UnsignedInt>::max)())
  74. {
  75. return client_errc::static_row_parsing_error;
  76. }
  77. output = static_cast<UnsignedInt>(v);
  78. return error_code();
  79. }
  80. // We want all integer types to be allowed as fields. Some integers
  81. // may have the same width as others, but different type (e.g. long and long long
  82. // may both be 64-bit, but different types). Auxiliar int_traits to allow this to work
  83. template <class T, bool is_signed = std::is_signed<T>::value, std::size_t width = sizeof(T)>
  84. struct int_traits
  85. {
  86. static constexpr bool is_supported = false;
  87. };
  88. template <class T>
  89. struct int_traits<T, true, 1>
  90. {
  91. static constexpr bool is_supported = true;
  92. static BOOST_INLINE_CONSTEXPR const char* type_name = "int8_t";
  93. static bool meta_check(meta_check_context& ctx)
  94. {
  95. switch (ctx.current_meta().type())
  96. {
  97. case column_type::tinyint: return !ctx.current_meta().is_unsigned();
  98. default: return false;
  99. }
  100. }
  101. static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
  102. };
  103. template <class T>
  104. struct int_traits<T, false, 1>
  105. {
  106. static constexpr bool is_supported = true;
  107. static BOOST_INLINE_CONSTEXPR const char* type_name = "uint8_t";
  108. static bool meta_check(meta_check_context& ctx)
  109. {
  110. switch (ctx.current_meta().type())
  111. {
  112. case column_type::tinyint: return ctx.current_meta().is_unsigned();
  113. default: return false;
  114. }
  115. }
  116. static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
  117. };
  118. template <class T>
  119. struct int_traits<T, true, 2>
  120. {
  121. static constexpr bool is_supported = true;
  122. static BOOST_INLINE_CONSTEXPR const char* type_name = "int16_t";
  123. static bool meta_check(meta_check_context& ctx)
  124. {
  125. switch (ctx.current_meta().type())
  126. {
  127. case column_type::tinyint: return true;
  128. case column_type::smallint:
  129. case column_type::year: return !ctx.current_meta().is_unsigned();
  130. default: return false;
  131. }
  132. }
  133. static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
  134. };
  135. template <class T>
  136. struct int_traits<T, false, 2>
  137. {
  138. static constexpr bool is_supported = true;
  139. static BOOST_INLINE_CONSTEXPR const char* type_name = "uint16_t";
  140. static bool meta_check(meta_check_context& ctx)
  141. {
  142. switch (ctx.current_meta().type())
  143. {
  144. case column_type::tinyint:
  145. case column_type::smallint:
  146. case column_type::year: return ctx.current_meta().is_unsigned();
  147. default: return false;
  148. }
  149. }
  150. static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
  151. };
  152. template <class T>
  153. struct int_traits<T, true, 4>
  154. {
  155. static constexpr bool is_supported = true;
  156. static BOOST_INLINE_CONSTEXPR const char* type_name = "int32_t";
  157. static bool meta_check(meta_check_context& ctx)
  158. {
  159. switch (ctx.current_meta().type())
  160. {
  161. case column_type::tinyint:
  162. case column_type::smallint:
  163. case column_type::year:
  164. case column_type::mediumint: return true;
  165. case column_type::int_: return !ctx.current_meta().is_unsigned();
  166. default: return false;
  167. }
  168. }
  169. static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
  170. };
  171. template <class T>
  172. struct int_traits<T, false, 4>
  173. {
  174. static constexpr bool is_supported = true;
  175. static BOOST_INLINE_CONSTEXPR const char* type_name = "uint32_t";
  176. static bool meta_check(meta_check_context& ctx)
  177. {
  178. switch (ctx.current_meta().type())
  179. {
  180. case column_type::tinyint:
  181. case column_type::smallint:
  182. case column_type::year:
  183. case column_type::mediumint:
  184. case column_type::int_: return ctx.current_meta().is_unsigned();
  185. default: return false;
  186. }
  187. }
  188. static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
  189. };
  190. template <class T>
  191. struct int_traits<T, true, 8>
  192. {
  193. static constexpr bool is_supported = true;
  194. static BOOST_INLINE_CONSTEXPR const char* type_name = "int64_t";
  195. static bool meta_check(meta_check_context& ctx)
  196. {
  197. switch (ctx.current_meta().type())
  198. {
  199. case column_type::tinyint:
  200. case column_type::smallint:
  201. case column_type::year:
  202. case column_type::mediumint:
  203. case column_type::int_: return true;
  204. case column_type::bigint: return !ctx.current_meta().is_unsigned();
  205. default: return false;
  206. }
  207. }
  208. static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
  209. };
  210. template <class T>
  211. struct int_traits<T, false, 8>
  212. {
  213. static constexpr bool is_supported = true;
  214. static BOOST_INLINE_CONSTEXPR const char* type_name = "uint64_t";
  215. static bool meta_check(meta_check_context& ctx)
  216. {
  217. switch (ctx.current_meta().type())
  218. {
  219. case column_type::tinyint:
  220. case column_type::smallint:
  221. case column_type::year:
  222. case column_type::mediumint:
  223. case column_type::int_:
  224. case column_type::bigint: return ctx.current_meta().is_unsigned();
  225. case column_type::bit: return true;
  226. default: return false;
  227. }
  228. }
  229. static error_code parse(field_view input, std::uint64_t& output)
  230. {
  231. return parse_unsigned_int(input, output);
  232. }
  233. };
  234. // Traits
  235. template <typename T, class EnableIf = void>
  236. struct readable_field_traits
  237. {
  238. static constexpr bool is_supported = false;
  239. };
  240. template <>
  241. struct readable_field_traits<char, void> : int_traits<char>
  242. {
  243. };
  244. template <>
  245. struct readable_field_traits<signed char, void> : int_traits<signed char>
  246. {
  247. };
  248. template <>
  249. struct readable_field_traits<unsigned char, void> : int_traits<unsigned char>
  250. {
  251. };
  252. template <>
  253. struct readable_field_traits<short, void> : int_traits<short>
  254. {
  255. };
  256. template <>
  257. struct readable_field_traits<unsigned short, void> : int_traits<unsigned short>
  258. {
  259. };
  260. template <>
  261. struct readable_field_traits<int, void> : int_traits<int>
  262. {
  263. };
  264. template <>
  265. struct readable_field_traits<unsigned int, void> : int_traits<unsigned int>
  266. {
  267. };
  268. template <>
  269. struct readable_field_traits<long, void> : int_traits<long>
  270. {
  271. };
  272. template <>
  273. struct readable_field_traits<unsigned long, void> : int_traits<unsigned long>
  274. {
  275. };
  276. template <>
  277. struct readable_field_traits<long long, void> : int_traits<long long>
  278. {
  279. };
  280. template <>
  281. struct readable_field_traits<unsigned long long, void> : int_traits<unsigned long long>
  282. {
  283. };
  284. template <>
  285. struct readable_field_traits<bool, void>
  286. {
  287. static constexpr bool is_supported = true;
  288. static BOOST_INLINE_CONSTEXPR const char* type_name = "bool";
  289. static bool meta_check(meta_check_context& ctx)
  290. {
  291. return ctx.current_meta().type() == column_type::tinyint && !ctx.current_meta().is_unsigned();
  292. }
  293. static error_code parse(field_view input, bool& output)
  294. {
  295. if (input.kind() != field_kind::int64)
  296. {
  297. return client_errc::static_row_parsing_error;
  298. }
  299. output = input.get_int64() != 0;
  300. return error_code();
  301. }
  302. };
  303. template <>
  304. struct readable_field_traits<float, void>
  305. {
  306. static constexpr bool is_supported = true;
  307. static BOOST_INLINE_CONSTEXPR const char* type_name = "float";
  308. static bool meta_check(meta_check_context& ctx)
  309. {
  310. return ctx.current_meta().type() == column_type::float_;
  311. }
  312. static error_code parse(field_view input, float& output)
  313. {
  314. if (input.kind() != field_kind::float_)
  315. {
  316. return client_errc::static_row_parsing_error;
  317. }
  318. output = input.get_float();
  319. return error_code();
  320. }
  321. };
  322. template <>
  323. struct readable_field_traits<double, void>
  324. {
  325. static constexpr bool is_supported = true;
  326. static BOOST_INLINE_CONSTEXPR const char* type_name = "double";
  327. static bool meta_check(meta_check_context& ctx)
  328. {
  329. switch (ctx.current_meta().type())
  330. {
  331. case column_type::float_:
  332. case column_type::double_: return true;
  333. default: return false;
  334. }
  335. }
  336. static error_code parse(field_view input, double& output)
  337. {
  338. auto kind = input.kind();
  339. if (kind == field_kind::float_)
  340. {
  341. output = input.get_float();
  342. return error_code();
  343. }
  344. else if (kind == field_kind::double_)
  345. {
  346. output = input.get_double();
  347. return error_code();
  348. }
  349. else
  350. {
  351. return client_errc::static_row_parsing_error;
  352. }
  353. }
  354. };
  355. template <class Allocator>
  356. struct readable_field_traits<std::basic_string<char, std::char_traits<char>, Allocator>, void>
  357. {
  358. static constexpr bool is_supported = true;
  359. static BOOST_INLINE_CONSTEXPR const char* type_name = "string";
  360. static bool meta_check(meta_check_context& ctx)
  361. {
  362. switch (ctx.current_meta().type())
  363. {
  364. case column_type::decimal:
  365. case column_type::char_:
  366. case column_type::varchar:
  367. case column_type::text:
  368. case column_type::enum_:
  369. case column_type::set:
  370. case column_type::json: return true;
  371. default: return false;
  372. }
  373. }
  374. static error_code parse(
  375. field_view input,
  376. std::basic_string<char, std::char_traits<char>, Allocator>& output
  377. )
  378. {
  379. if (input.kind() != field_kind::string)
  380. {
  381. return client_errc::static_row_parsing_error;
  382. }
  383. output = input.get_string();
  384. return error_code();
  385. }
  386. };
  387. template <class Allocator>
  388. struct readable_field_traits<std::vector<unsigned char, Allocator>, void>
  389. {
  390. static constexpr bool is_supported = true;
  391. static BOOST_INLINE_CONSTEXPR const char* type_name = "blob";
  392. static bool meta_check(meta_check_context& ctx)
  393. {
  394. switch (ctx.current_meta().type())
  395. {
  396. case column_type::binary:
  397. case column_type::varbinary:
  398. case column_type::blob:
  399. case column_type::geometry:
  400. case column_type::unknown: return true;
  401. default: return false;
  402. }
  403. }
  404. static error_code parse(field_view input, std::vector<unsigned char, Allocator>& output)
  405. {
  406. if (input.kind() != field_kind::blob)
  407. {
  408. return client_errc::static_row_parsing_error;
  409. }
  410. auto view = input.get_blob();
  411. output.assign(view.begin(), view.end());
  412. return error_code();
  413. }
  414. };
  415. template <>
  416. struct readable_field_traits<date, void>
  417. {
  418. static constexpr bool is_supported = true;
  419. static BOOST_INLINE_CONSTEXPR const char* type_name = "date";
  420. static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::date; }
  421. static error_code parse(field_view input, date& output)
  422. {
  423. if (input.kind() != field_kind::date)
  424. {
  425. return client_errc::static_row_parsing_error;
  426. }
  427. output = input.get_date();
  428. return error_code();
  429. }
  430. };
  431. template <>
  432. struct readable_field_traits<datetime, void>
  433. {
  434. static constexpr bool is_supported = true;
  435. static BOOST_INLINE_CONSTEXPR const char* type_name = "datetime";
  436. static bool meta_check(meta_check_context& ctx)
  437. {
  438. switch (ctx.current_meta().type())
  439. {
  440. case column_type::datetime:
  441. case column_type::timestamp: return true;
  442. default: return false;
  443. }
  444. }
  445. static error_code parse(field_view input, datetime& output)
  446. {
  447. if (input.kind() != field_kind::datetime)
  448. {
  449. return client_errc::static_row_parsing_error;
  450. }
  451. output = input.get_datetime();
  452. return error_code();
  453. }
  454. };
  455. template <>
  456. struct readable_field_traits<time, void>
  457. {
  458. static constexpr bool is_supported = true;
  459. static BOOST_INLINE_CONSTEXPR const char* type_name = "time";
  460. static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::time; }
  461. static error_code parse(field_view input, time& output)
  462. {
  463. if (input.kind() != field_kind::time)
  464. {
  465. return client_errc::static_row_parsing_error;
  466. }
  467. output = input.get_time();
  468. return error_code();
  469. }
  470. };
  471. // std::optional<T> and boost::optional<T>. To avoid dependencies,
  472. // this is achieved through a "concept"
  473. template <class T, class = void>
  474. struct is_readable_optional : std::false_type
  475. {
  476. };
  477. template <class T>
  478. struct is_readable_optional<
  479. T,
  480. void_t<
  481. typename std::enable_if<
  482. std::is_same<decltype(std::declval<T&>().value()), typename T::value_type&>::value>::type,
  483. decltype(std::declval<T&>().emplace()), // T should be default constructible
  484. decltype(std::declval<T&>().reset())>> : std::true_type
  485. {
  486. };
  487. template <class T>
  488. struct readable_field_traits<
  489. T,
  490. typename std::enable_if<
  491. is_readable_optional<T>::value && readable_field_traits<typename T::value_type>::is_supported>::type>
  492. {
  493. using value_type = typename T::value_type;
  494. static constexpr bool is_supported = true;
  495. static BOOST_INLINE_CONSTEXPR const char* type_name = readable_field_traits<value_type>::type_name;
  496. static bool meta_check(meta_check_context& ctx)
  497. {
  498. ctx.set_nullability_checked();
  499. return readable_field_traits<value_type>::meta_check(ctx);
  500. }
  501. static error_code parse(field_view input, T& output)
  502. {
  503. if (input.is_null())
  504. {
  505. output.reset();
  506. return error_code();
  507. }
  508. else
  509. {
  510. output.emplace();
  511. return readable_field_traits<value_type>::parse(input, output.value());
  512. }
  513. }
  514. };
  515. template <class T>
  516. struct is_readable_field
  517. {
  518. static constexpr bool value = readable_field_traits<T>::is_supported;
  519. };
  520. template <typename ReadableField>
  521. void meta_check_field_impl(meta_check_context& ctx)
  522. {
  523. using traits_t = readable_field_traits<ReadableField>;
  524. // Verify that the field is present
  525. if (ctx.is_current_field_absent())
  526. {
  527. ctx.add_field_absent_error();
  528. return;
  529. }
  530. // Perform the check
  531. bool ok = traits_t::meta_check(ctx);
  532. if (!ok)
  533. {
  534. ctx.add_type_mismatch_error(traits_t::type_name);
  535. }
  536. // Check nullability
  537. if (!ctx.nullability_checked() && !ctx.current_meta().is_not_null())
  538. {
  539. ctx.add_nullability_error();
  540. }
  541. }
  542. template <typename ReadableField>
  543. void meta_check_field(meta_check_context& ctx)
  544. {
  545. static_assert(is_readable_field<ReadableField>::value, "Should be a ReadableField");
  546. meta_check_field_impl<ReadableField>(ctx);
  547. ctx.advance();
  548. }
  549. } // namespace detail
  550. } // namespace mysql
  551. } // namespace boost
  552. #endif