MySQL 9.6.0
Source Code Documentation
decode.h
Go to the documentation of this file.
1// Copyright (c) 2025, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
24#ifndef MYSQL_SETS_STRCONV_DECODE_H
25#define MYSQL_SETS_STRCONV_DECODE_H
26
27/// @file
28/// Experimental API header
29
30#include "mysql/sets/boundary_set_meta.h" // Is_discrete_set_traits
31#include "mysql/sets/interval.h" // detail::Relaxed_interval
32#include "mysql/sets/interval_set_meta.h" // Is_interval_container
33#include "mysql/sets/strconv/boundary_set_text_format.h" // Boundary_set_text_format
34#include "mysql/strconv/strconv.h" // Parser
35
36/// @addtogroup GroupLibsMysqlSets
37/// @{
38
40
41/// Read an interval into the output Relaxed_interval, checking that the
42/// boundaries are in range, but not that they are in order. The Set traits must
43/// be _discrete_ (see Is_discrete_set_traits).
44///
45/// The format is one of "start<boundary_separator>inclusive_end" or "start".
46/// Note that the text format, contrary to all other places, stores the
47/// *inclusive* end value. If the end value is omitted, the inclusive end is
48/// equal to start, i.e., a singleton interval.
49///
50/// This function requires that the start boundary is in the range defined in
51/// Set_traits_t. If an end value is given, it requires that that the exclusive
52/// endpoint is not less than the minimum inclusive value, and not greater than
53/// the maximum exclusive value. It does not require that the start boundary is
54/// smaller than the end boundary.
55///
56/// @param format Format type tag used to read boundary values.
57///
58/// @param[in,out] parser reference to Parser.
59///
60/// @param[out] out Destination interval.
61template <mysql::sets::Is_discrete_set_traits Set_traits_t>
63 const Is_format auto &format, mysql::strconv::Parser &parser,
65 using Element_t = typename Set_traits_t::Element_t;
67
68 Element_t start{};
69 Element_t inclusive_end{};
70 parser
71 .fluent(format) //
72 .read(start) // START
73 .check_prev_token([&] { // check START
74 if (!Set_traits_t::in_range(start)) {
75 parser.set_parse_error("Interval start out of range");
76 }
77 inclusive_end = start;
78 }) //
79 .end_optional() // String may end here
80 .literal(format.m_boundary_separator) // "-"
81 .read(inclusive_end) // END
82 .check_prev_token([&] { // check END
83 if (Set_traits_t::ge(inclusive_end, Set_traits_t::max_exclusive()) ||
84 Set_traits_t::lt(Set_traits_t::next(inclusive_end),
85 Set_traits_t::min())) {
86 parser.set_parse_error("Interval end out of range");
87 }
88 });
89 if (parser.is_ok()) {
90 out.assign(start, Set_traits_t::next(inclusive_end));
91 }
92}
93
94/// Read one interval in text format into an output container with a cursor. The
95/// Set traits must be _discrete_ (see Is_discrete_set_traits).
96///
97/// The format is one of "start<boundary_separator>inclusive_end" or "start".
98/// Note that the text format, contrary to all other places, stores the
99/// *inclusive* end value. If the end value is omitted, the inclusive end is
100/// equal to start.
101///
102/// This function requires that the start boundary is in the range defined in
103/// Set_traits_t. If an (inclusive) end value is given, it also requires that
104/// the corresponding *exclusive* end is not less than the minimum value. If the
105/// start boundary is not less than the end boundary, the interval counts as
106/// valid and empty and will not be inserted in the output.
107///
108/// @param format Format type tag used to read Relaxed_interval objects.
109///
110/// @param[in,out] parser reference to Parser.
111///
112/// @param[in,out] out Pair where the first component is a reference to the
113/// destination container and the second component is a reference to a cursor to
114/// the suggested insertion position.
115template <mysql::sets::Is_boundary_container Boundary_container_t>
117 typename Boundary_container_t::Set_traits_t>
119 const Is_format auto &format, mysql::strconv::Parser &parser,
120 const std::pair<Boundary_container_t &,
121 typename Boundary_container_t::Iterator_t &> &out) {
122 using Set_traits_t = typename Boundary_container_t::Set_traits_t;
123 Boundary_container_t &boundary_container = out.first;
124 auto &cursor = out.second;
127 if (Set_traits_t::gt(interval.exclusive_end(), interval.start())) {
128 if (mysql::utils::call_and_catch([&]() DEDUCED_NOEXCEPT_FUNCTION(
129 boundary_container.inplace_union(cursor, interval.start(),
130 interval.exclusive_end()))) ==
132 parser.set_oom();
133 }
134 }
135}
136
137/// Parse from text format into a boundary container. The Set traits must be
138/// _discrete_ (see Is_discrete_set_traits).
139///
140/// The format is "((<interval>)?<interval_separator>)*". See @c
141/// interval_from_text for the format of `interval`.
142///
143/// @param format Format type tag used to read intervals into the container.
144///
145/// @param[in,out] parser reference to Parser.
146///
147/// @param[in,out] out Destination container.
148template <mysql::sets::Is_boundary_container Boundary_container_t>
150 typename Boundary_container_t::Set_traits_t>
151void boundary_set_from_text(const Is_format auto &format,
153 Boundary_container_t &out) {
154 // Require fast insertions to avoid degenerate quadratic execution time if
155 // intervals are out of order.
156 static_assert(
157 Boundary_container_t::has_fast_insertion,
158 "Use a boundary container type that supports less-than-linear-time "
159 "insertion operations, such as Map_boundary_container.");
160 auto separator = format.m_interval_separator;
161 auto position = out.begin();
162 std::pair<Boundary_container_t &, decltype(position) &> interval_cursor(
163 out, position);
164
168 if (format.m_allow_redundant_separators ==
173 }
174 std::size_t min_count =
175 (format.m_allow_empty == mysql::strconv::Allow_empty::yes) ? 0 : 1;
176 parser.fluent(format).read_repeated_with_separators(
177 interval_cursor, separator, mysql::strconv::Repeat::at_least(min_count),
178 repeated, leading, trailing);
179}
180
181/// Parse from binary format (with variable-length integers) into a boundary
182/// container. The Set traits must be _discrete_ and _metric_ (see
183/// Is_discrete_metric_set_traits).
184///
185/// The format is equal to that described for boundary_set_to_binary.
186///
187/// @param format Format type tag used to read boundary values.
188///
189/// @param[in,out] parser reference to Parser.
190///
191/// @param[out] out Destination container.
192template <mysql::sets::Is_boundary_container Boundary_container_t>
194 typename Boundary_container_t::Set_traits_t>
195void boundary_set_from_binary(const Is_format auto &format,
197 Boundary_container_t &out) {
198 using Set_traits_t = typename Boundary_container_t::Set_traits_t;
199 using Element_t = typename Set_traits_t::Element_t;
200 using Difference_t = typename Set_traits_t::Difference_t;
202
203 // The number of remaining boundaries to read.
204 uint64_t remaining{};
205
206 // Sanity-check: the number of boundaries can't be bigger than the remaining
207 // bytes.
208 auto check_remaining = mysql::strconv::Checker([&] {
209 if (remaining > parser.remaining_size()) {
210 parser.set_parse_error(
211 "The value stored in the size field exceeds the number of remaining "
212 "bytes");
213 }
214 });
215
216 // Read the number of boundaries.
217 if (parser.read(format | check_remaining, remaining) != Return_status::ok)
218 return;
219
220 // Cursor to output container.
221 auto cursor = out.begin();
222
223 // Smallest allowed value for next boundary we read from the container.
224 // Because of the optimization that skips the first value if it equals
225 // Set_traits_t::min(), the smallest allowed first boundary stored explicitly
226 // is min+1.
227 Element_t next_min = Set_traits_t::next(Set_traits_t::min());
228
229 // Value to read.
230 Difference_t delta{};
231
232 // Check that the delta is in range.
233 auto check_delta = mysql::strconv::Checker([&] {
234 if (delta > Set_traits_t::sub(Set_traits_t::max_exclusive(), next_min)) {
235 parser.set_parse_error("Value exceeds maximum");
236 }
237 });
238
239 // Read one boundary into out. Return ok or error.
240 auto read_next = [&](Element_t &element) {
241 if (parser.read(format | check_delta, delta) != Return_status::ok)
243 element = Set_traits_t::add(next_min, delta);
244 next_min = Set_traits_t::next(element);
245 return Return_status::ok;
246 };
247
248 // Insert the given interval into the container. Return ok or error.
249 auto insert = [&](const Element_t &start, const Element_t &exclusive_end) {
250 auto ret = mysql::utils::call_and_catch([&]() DEDUCED_NOEXCEPT_FUNCTION(
251 out.inplace_union(cursor, start, exclusive_end)));
252 if (ret != Return_status::ok) parser.set_oom();
253 return ret;
254 };
255
256 // Special case to read the endpoint of the first interval, in case
257 // 'remaining' is odd.
258 if ((remaining & 1) == 1) {
259 Element_t exclusive_end{Set_traits_t::min()};
260 if (read_next(exclusive_end) != Return_status::ok) return;
261 if (insert(Set_traits_t::min(), exclusive_end) != Return_status::ok) return;
262 --remaining;
263 }
264
265 // Read two values at a time and insert them into the container.
266 while (remaining != 0) {
267 Element_t start;
268 if (read_next(start) != Return_status::ok) return;
269 Element_t exclusive_end;
270 if (read_next(exclusive_end) != Return_status::ok) return;
271 if (insert(start, exclusive_end) != Return_status::ok) return;
272 remaining -= 2;
273 }
274}
275
276/// Parse from binary format (with fixed-length integers) into a boundary
277/// container. The Set traits must be _bounded_ (see Is_bounded_set_traits).
278///
279/// The format is equal to that described for boundary_set_to_binary.
280///
281/// @param format Format type tag used to read boundary values.
282///
283/// @param[in,out] parser reference to Parser.
284///
285/// @param[out] out Destination container.
286template <mysql::sets::Is_boundary_container Boundary_container_t>
288 typename Boundary_container_t::Set_traits_t>
289void boundary_set_from_binary_fixint(const Is_format auto &format,
291 Boundary_container_t &out) {
292 using Set_traits_t = typename Boundary_container_t::Set_traits_t;
293 using Element_t = typename Set_traits_t::Element_t;
295
296 // The number of boundaries to read.
297 uint64_t remaining_intervals{};
298
299 // Sanity-check: the number of intervals times the encoded size of an interval
300 // can't be bigger than the remaining bytes.
301 auto check_remaining = mysql::strconv::Checker([&] {
302 if (remaining_intervals * 8 * 2 > parser.remaining_size()) {
303 parser.set_parse_error(
304 "The value stored in the size field exceeds the number of values "
305 "that fit in the remaining string");
306 }
307 });
308
309 // Read the number of boundaries.
310 if (parser.read(format | check_remaining, remaining_intervals) !=
311 Return_status::ok)
312 return;
313
314 // Cursor to output container.
315 auto cursor = out.begin();
316 // Smallest allowed value for next boundary.
317 Element_t last_value{};
318 // True only while processing the first boundary.
319 bool first{true};
320
321 // Read one boundary into `element`. Return ok or error.
322 auto read_next = [&](Element_t &value) {
323 auto check_value = mysql::strconv::Checker([&] {
324 if (first) {
325 if (Set_traits_t::lt(value, Set_traits_t::min()))
326 parser.set_parse_error("Value is less than minimum");
327 } else {
328 if (Set_traits_t::le(value, last_value))
329 parser.set_parse_error(
330 "Value is less than or equal to previous value");
331 }
332 if (Set_traits_t::gt(value, Set_traits_t::max_exclusive()))
333 parser.set_parse_error("Value exceeds maximum");
334 });
335
336 if (parser.read(format | check_value, value) != Return_status::ok)
338 last_value = value;
339 first = false;
340 return Return_status::ok;
341 };
342
343 // Insert the given interval into the container. Return ok or error.
344 auto insert = [&](Element_t start, Element_t exclusive_end) {
345 auto ret = mysql::utils::call_and_catch([&]() DEDUCED_NOEXCEPT_FUNCTION(
346 out.inplace_union(cursor, start, exclusive_end)));
347 if (ret != Return_status::ok) parser.set_oom();
348 return ret;
349 };
350
351 // Read two values at a time and insert them into the container.
352 while (remaining_intervals) {
353 Element_t start;
354 if (read_next(start) != Return_status::ok) return;
355 Element_t exclusive_end;
356 if (read_next(exclusive_end) != Return_status::ok) return;
357 if (insert(start, exclusive_end) != Return_status::ok) return;
358 --remaining_intervals;
359 }
360}
361
362/// Parse into an interval container, advance the position, and return the
363/// status.
364///
365/// See above for details.
366template <mysql::sets::Is_interval_container Interval_container_t>
367void decode_interval_set(const Is_format auto &format,
369 Interval_container_t &out) {
370 std::ignore = parser.read(format, out.boundaries());
371}
372
373} // namespace mysql::sets::strconv
374
375// Glue to make mysql::strconv find the parsers.
376namespace mysql::strconv {
377
378/// Enable mysql::strconv::decode(Text_format, Relaxed_interval), for
379/// boundary containers whose Set traits that are _discrete_.
380template <mysql::sets::Is_discrete_set_traits Set_traits_t>
385}
386
387/// Enable mysql::strconv::decode(Text_format,
388/// std::pair<Boundary_container, cursor>), for boundary containers whose Set
389/// traits that are _discrete_.
390template <mysql::sets::Is_boundary_container Boundary_container_t>
392 typename Boundary_container_t::Set_traits_t>
395 const std::pair<Boundary_container_t &,
396 typename Boundary_container_t::Iterator_t &> &out) {
398}
399
400/// Enable mysql::strconv::decode(Text_format, Boundary_container), for
401/// boundary containers whose Set traits that are _discrete_.
402template <mysql::sets::Is_boundary_container Boundary_container_t>
404 typename Boundary_container_t::Set_traits_t>
406 mysql::strconv::Parser &parser, Boundary_container_t &out) {
408}
409
410/// Enable mysql::strconv::decode(Binary_format, Boundary_container),
411/// for boundary containers that are _discrete_ and _metric_.
412template <mysql::sets::Is_boundary_container Boundary_container_t>
414 typename Boundary_container_t::Set_traits_t>
416 Boundary_container_t &out) {
418}
419
420/// Enable mysql::strconv::decode(Fixint_binary_format,
421/// Boundary_container), for boundary containers that are _discrete_.
422template <mysql::sets::Is_boundary_container Boundary_container_t>
424 typename Boundary_container_t::Set_traits_t>
426 mysql::strconv::Parser &parser, Boundary_container_t &out) {
428}
429
430/// Enable mysql::strconv::decode(/*Format*/, Interval_container), by
431/// reading the boundaries in the given format.
432template <mysql::sets::Is_interval_container Interval_container_t>
434 Interval_container_t &out) {
436}
437
438} // namespace mysql::strconv
439
440// addtogroup GroupLibsMysqlSets
441/// @}
442
443#endif // ifndef MYSQL_SETS_STRCONV_DECODE_H
Experimental API header.
Experimental API header.
#define DEDUCED_NOEXCEPT_FUNCTION(X)
Helper macro to define a function that returns the result of a single expression, and has a condition...
Definition: call_and_catch.h:151
Holds the start boundary and endpoint boundary of an interval.
Definition: interval.h:140
void assign(const Element_t &start_arg, const Element_t &exclusive_end_arg)
Set both boundaries to the given values, without validating the range.
Definition: interval.h:86
Class holding a checker function, used to check the validity of a parsed value.
Definition: checker.h:57
Object used to parse strings.
Definition: parser.h:69
static Repeat at_least(Int_t min_arg)
Return a Repeat object representing min_arg or more repetitions (bounded only by std::numeric_limits)
Definition: repeat.h:145
True if Test is a "bounded" Set traits class.
Definition: set_traits.h:105
True if Test satisfies both Is_discrete_set_traits and Is_metric_set_traits.
Definition: set_traits.h:151
True if Test is a "discrete" Set traits class, i.e., it bounded, and it is possible to compute succes...
Definition: set_traits.h:119
True if Test is a format.
Definition: format.h:42
static void start(mysql_harness::PluginFuncEnv *env)
Definition: http_auth_backend_plugin.cc:180
Experimental API header.
Experimental API header.
static const char separator
Definition: item_func.cc:4532
Experimental API header.
static int interval
Definition: mysqladmin.cc:72
void error(const char *format,...)
struct Parser parser
Type sub(Shards< COUNT > &shards, size_t id, size_t n)
Decrement the counter for a shard by n.
Definition: ut0counter.h:280
std::string format(const routing_guidelines::Session_info &session_info, bool extended_session_info)
Definition: dest_metadata_cache.cc:170
void insert(dd::cache::SPI_lru_cache_owner_ptr &c, dd::Object_id id)
Definition: dictionary_client.cc:3077
ValueType value(const std::optional< ValueType > &v)
Definition: gtid.h:83
Definition: decode.h:39
void interval_from_text(const Is_format auto &format, mysql::strconv::Parser &parser, mysql::sets::detail::Relaxed_interval< Set_traits_t > &out)
Read an interval into the output Relaxed_interval, checking that the boundaries are in range,...
Definition: decode.h:62
void decode_interval_set(const Is_format auto &format, mysql::strconv::Parser &parser, Interval_container_t &out)
Parse into an interval container, advance the position, and return the status.
Definition: decode.h:367
void boundary_set_from_binary_fixint(const Is_format auto &format, mysql::strconv::Parser &parser, Boundary_container_t &out)
Parse from binary format (with fixed-length integers) into a boundary container.
Definition: decode.h:289
void boundary_set_from_binary(const Is_format auto &format, mysql::strconv::Parser &parser, Boundary_container_t &out)
Parse from binary format (with variable-length integers) into a boundary container.
Definition: decode.h:195
void boundary_set_from_text(const Is_format auto &format, mysql::strconv::Parser &parser, Boundary_container_t &out)
Parse from text format into a boundary container.
Definition: decode.h:151
Definition: gtid_binary_format.h:41
void decode_impl(const Gtid_binary_format &format, Parser &parser, mysql::gtids::Is_tag auto &tag)
Definition: gtid_binary_format_conv.h:63
Return_status
Simple, strongly-typed enumeration to indicate internal status: ok, error.
Definition: return_status.h:40
@ ok
operation succeeded
@ error
operation failed
static mysql_service_status_t add(reference_caching_channel channel, const char *implementation_name) noexcept
Definition: component.cc:127
Format tag to identify binary format.
Definition: binary_format.h:38
Tag to identify the formatting algorithm for boundary sets of integrals, and provide the separator st...
Definition: boundary_set_text_format.h:51
Format tag to identify fixed-length-integer binary format.
Definition: fixint_binary_format.h:42