deserialize_text_field.ipp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. //
  2. // Copyright (c) 2019-2023 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_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_IPP
  8. #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_IPP
  9. #pragma once
  10. #include <boost/mysql/blob_view.hpp>
  11. #include <boost/mysql/datetime.hpp>
  12. #include <boost/mysql/field_view.hpp>
  13. #include <boost/mysql/metadata.hpp>
  14. #include <boost/mysql/string_view.hpp>
  15. #include <boost/mysql/detail/config.hpp>
  16. #include <boost/mysql/detail/datetime.hpp>
  17. #include <boost/mysql/impl/internal/protocol/bit_deserialization.hpp>
  18. #include <boost/mysql/impl/internal/protocol/constants.hpp>
  19. #include <boost/mysql/impl/internal/protocol/deserialize_text_field.hpp>
  20. #include <boost/mysql/impl/internal/protocol/serialization.hpp>
  21. #include <boost/assert.hpp>
  22. #include <boost/lexical_cast/try_lexical_convert.hpp>
  23. #include <cmath>
  24. #include <cstddef>
  25. #include <cstdlib>
  26. #include <type_traits>
  27. namespace boost {
  28. namespace mysql {
  29. namespace detail {
  30. #ifdef BOOST_MSVC
  31. #pragma warning(push)
  32. #pragma warning(disable : 4996) // MSVC doesn't like my sscanf's
  33. #endif
  34. // Constants
  35. BOOST_MYSQL_STATIC_IF_COMPILED constexpr unsigned max_decimals = 6u;
  36. namespace textc {
  37. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t year_sz = 4;
  38. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t month_sz = 2;
  39. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t day_sz = 2;
  40. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t hours_min_sz = 2; // in TIME, it may be longer
  41. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t mins_sz = 2;
  42. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t secs_sz = 2;
  43. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t date_sz = year_sz + month_sz + day_sz + 2; // delimiters
  44. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t time_min_sz = hours_min_sz + mins_sz + secs_sz +
  45. 2; // delimiters
  46. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t time_max_sz = time_min_sz + max_decimals +
  47. 3; // sign, period, hour extra character
  48. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t datetime_min_sz = date_sz + time_min_sz +
  49. 1; // delimiter
  50. BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t datetime_max_sz = datetime_min_sz + max_decimals +
  51. 1; // period
  52. BOOST_MYSQL_STATIC_IF_COMPILED constexpr unsigned time_max_hour = 838;
  53. } // namespace textc
  54. // Integers
  55. template <class T>
  56. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  57. deserialize_text_value_int_impl(string_view from, field_view& to) noexcept
  58. {
  59. T v;
  60. bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), v);
  61. if (!ok)
  62. return deserialize_errc::protocol_value_error;
  63. to = field_view(v);
  64. return deserialize_errc::ok;
  65. }
  66. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  67. deserialize_text_value_int(string_view from, field_view& to, const metadata& meta) noexcept
  68. {
  69. return meta.is_unsigned() ? deserialize_text_value_int_impl<std::uint64_t>(from, to)
  70. : deserialize_text_value_int_impl<std::int64_t>(from, to);
  71. }
  72. // Floating points
  73. template <class T>
  74. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  75. deserialize_text_value_float(string_view from, field_view& to) noexcept
  76. {
  77. T val;
  78. bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), val);
  79. if (!ok || std::isnan(val) || std::isinf(val)) // SQL std forbids these values
  80. return deserialize_errc::protocol_value_error;
  81. to = field_view(val);
  82. return deserialize_errc::ok;
  83. }
  84. // Strings
  85. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  86. deserialize_text_value_string(string_view from, field_view& to) noexcept
  87. {
  88. to = field_view(from);
  89. return deserialize_errc::ok;
  90. }
  91. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  92. deserialize_text_value_blob(string_view from, field_view& to) noexcept
  93. {
  94. to = field_view(to_span(from));
  95. return deserialize_errc::ok;
  96. }
  97. // Date/time types
  98. BOOST_MYSQL_STATIC_OR_INLINE unsigned sanitize_decimals(unsigned decimals) noexcept
  99. {
  100. return (std::min)(decimals, max_decimals);
  101. }
  102. // Computes the meaning of the parsed microsecond number, taking into
  103. // account decimals (85 with 2 decimals means 850000us)
  104. BOOST_MYSQL_STATIC_OR_INLINE unsigned compute_micros(unsigned parsed_micros, unsigned decimals) noexcept
  105. {
  106. return parsed_micros * static_cast<unsigned>(std::pow(10, max_decimals - decimals));
  107. }
  108. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc deserialize_text_ymd(string_view from, date& to)
  109. {
  110. using namespace textc;
  111. // Size check
  112. if (from.size() != date_sz)
  113. return deserialize_errc::protocol_value_error;
  114. // Copy to a NULL-terminated buffer
  115. char buffer[date_sz + 1]{};
  116. std::memcpy(buffer, from.data(), from.size());
  117. // Parse individual components
  118. unsigned year, month, day;
  119. char extra_char;
  120. int parsed = sscanf(buffer, "%4u-%2u-%2u%c", &year, &month, &day, &extra_char);
  121. if (parsed != 3)
  122. return deserialize_errc::protocol_value_error;
  123. // Range check for individual components. MySQL doesn't allow invidiual components
  124. // to be out of range, although they may be zero or representing an invalid date
  125. if (year > max_year || month > max_month || day > max_day)
  126. return deserialize_errc::protocol_value_error;
  127. to = date(
  128. static_cast<std::uint16_t>(year),
  129. static_cast<std::uint8_t>(month),
  130. static_cast<std::uint8_t>(day)
  131. );
  132. return deserialize_errc::ok;
  133. }
  134. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  135. deserialize_text_value_date(string_view from, field_view& to) noexcept
  136. {
  137. date d;
  138. auto err = deserialize_text_ymd(from, d);
  139. if (err != deserialize_errc::ok)
  140. return err;
  141. to = field_view(d);
  142. return deserialize_errc::ok;
  143. }
  144. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  145. deserialize_text_value_datetime(string_view from, field_view& to, const metadata& meta) noexcept
  146. {
  147. using namespace textc;
  148. // Sanitize decimals
  149. unsigned decimals = sanitize_decimals(meta.decimals());
  150. // Length check
  151. std::size_t expected_size = datetime_min_sz + (decimals ? decimals + 1 : 0);
  152. if (from.size() != expected_size)
  153. return deserialize_errc::protocol_value_error;
  154. // Deserialize date part
  155. date d;
  156. auto err = deserialize_text_ymd(from.substr(0, date_sz), d);
  157. if (err != deserialize_errc::ok)
  158. return err;
  159. // Copy to NULL-terminated buffer
  160. constexpr std::size_t datetime_time_first = date_sz + 1; // date + space
  161. char buffer[datetime_max_sz - datetime_time_first + 1]{};
  162. std::memcpy(buffer, from.data() + datetime_time_first, from.size() - datetime_time_first);
  163. // Parse
  164. unsigned hours, minutes, seconds;
  165. unsigned micros = 0;
  166. char extra_char;
  167. if (decimals)
  168. {
  169. int parsed = sscanf(buffer, "%2u:%2u:%2u.%6u%c", &hours, &minutes, &seconds, &micros, &extra_char);
  170. if (parsed != 4)
  171. return deserialize_errc::protocol_value_error;
  172. micros = compute_micros(micros, decimals);
  173. }
  174. else
  175. {
  176. int parsed = sscanf(buffer, "%2u:%2u:%2u%c", &hours, &minutes, &seconds, &extra_char);
  177. if (parsed != 3)
  178. return deserialize_errc::protocol_value_error;
  179. }
  180. // Validity check. Although MySQL allows invalid and zero datetimes, it doesn't allow
  181. // individual components to be out of range.
  182. if (hours > max_hour || minutes > max_min || seconds > max_sec || micros > max_micro)
  183. {
  184. return deserialize_errc::protocol_value_error;
  185. }
  186. datetime dt(
  187. d.year(),
  188. d.month(),
  189. d.day(),
  190. static_cast<std::uint8_t>(hours),
  191. static_cast<std::uint8_t>(minutes),
  192. static_cast<std::uint8_t>(seconds),
  193. static_cast<std::uint32_t>(micros)
  194. );
  195. to = field_view(dt);
  196. return deserialize_errc::ok;
  197. }
  198. BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
  199. deserialize_text_value_time(string_view from, field_view& to, const metadata& meta) noexcept
  200. {
  201. using namespace textc;
  202. // Sanitize decimals
  203. unsigned decimals = sanitize_decimals(meta.decimals());
  204. // size check
  205. std::size_t actual_min_size = time_min_sz + (decimals ? decimals + 1 : 0);
  206. std::size_t actual_max_size = actual_min_size + 1 + 1; // hour extra character and sign
  207. BOOST_ASSERT(actual_max_size <= time_max_sz);
  208. if (from.size() < actual_min_size || from.size() > actual_max_size)
  209. return deserialize_errc::protocol_value_error;
  210. // Copy to NULL-terminated buffer
  211. char buffer[time_max_sz + 1]{};
  212. memcpy(buffer, from.data(), from.size());
  213. // Sign
  214. bool is_negative = from[0] == '-';
  215. const char* first = is_negative ? buffer + 1 : buffer;
  216. // Parse it
  217. unsigned hours, minutes, seconds;
  218. unsigned micros = 0;
  219. char extra_char;
  220. if (decimals)
  221. {
  222. int parsed = sscanf(first, "%3u:%2u:%2u.%6u%c", &hours, &minutes, &seconds, &micros, &extra_char);
  223. if (parsed != 4)
  224. return deserialize_errc::protocol_value_error;
  225. micros = compute_micros(micros, decimals);
  226. }
  227. else
  228. {
  229. int parsed = sscanf(first, "%3u:%2u:%2u%c", &hours, &minutes, &seconds, &extra_char);
  230. if (parsed != 3)
  231. return deserialize_errc::protocol_value_error;
  232. }
  233. // Range check
  234. if (hours > time_max_hour || minutes > max_min || seconds > max_sec || micros > max_micro)
  235. {
  236. return deserialize_errc::protocol_value_error;
  237. }
  238. // Sum it
  239. auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) + std::chrono::seconds(seconds) +
  240. std::chrono::microseconds(micros);
  241. if (is_negative)
  242. {
  243. res = -res;
  244. }
  245. // Done
  246. to = field_view(res);
  247. return deserialize_errc::ok;
  248. }
  249. } // namespace detail
  250. } // namespace mysql
  251. } // namespace boost
  252. boost::mysql::detail::deserialize_errc boost::mysql::detail::deserialize_text_field(
  253. string_view from,
  254. const metadata& meta,
  255. field_view& output
  256. )
  257. {
  258. switch (meta.type())
  259. {
  260. case column_type::tinyint:
  261. case column_type::smallint:
  262. case column_type::mediumint:
  263. case column_type::int_:
  264. case column_type::bigint:
  265. case column_type::year: return deserialize_text_value_int(from, output, meta);
  266. case column_type::bit: return deserialize_bit(from, output);
  267. case column_type::float_: return deserialize_text_value_float<float>(from, output);
  268. case column_type::double_: return deserialize_text_value_float<double>(from, output);
  269. case column_type::timestamp:
  270. case column_type::datetime: return deserialize_text_value_datetime(from, output, meta);
  271. case column_type::date: return deserialize_text_value_date(from, output);
  272. case column_type::time: return deserialize_text_value_time(from, output, meta);
  273. // True string types
  274. case column_type::char_:
  275. case column_type::varchar:
  276. case column_type::text:
  277. case column_type::enum_:
  278. case column_type::set:
  279. case column_type::decimal:
  280. case column_type::json: return deserialize_text_value_string(from, output);
  281. // Blobs and anything else
  282. case column_type::binary:
  283. case column_type::varbinary:
  284. case column_type::blob:
  285. case column_type::geometry:
  286. default: return deserialize_text_value_blob(from, output);
  287. }
  288. }
  289. #endif