1 /*
2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
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
23 * 02110-1301 USA
24 */
25
26 #include "ngs/protocol/row_builder.h"
27
28 #include "ngs/protocol/output_buffer.h"
29
30 #include "ngs_common/xdatetime.h"
31 #include "ngs_common/xdecimal.h"
32
33 #include "decimal.h"
34
35 #include "ngs_common/protocol_protobuf.h"
36 #include <iostream>
37 #include <string>
38 #include <limits>
39 #include <algorithm>
40 #include <cstring>
41
42 using namespace ngs;
43
44 #define ADD_FIELD_HEADER() \
45 assert(m_row_processing); \
46 google::protobuf::internal::WireFormatLite::WriteTag( \
47 1, \
48 google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, \
49 m_out_stream.get() \
50 ); \
51 ++m_num_fields;
52
53
Row_builder()54 Row_builder::Row_builder():
55 m_row_processing(false)
56 {}
57
~Row_builder()58 Row_builder::~Row_builder()
59 {
60 abort_row();
61 }
62
abort_row()63 void Row_builder::abort_row()
64 {
65 if (m_row_processing)
66 {
67 m_out_stream.reset();
68 m_out_buffer->rollback();
69 m_row_processing = false;
70 }
71 }
72
start_row(Output_buffer * out_buffer)73 void Row_builder::start_row(Output_buffer* out_buffer)
74 {
75 m_num_fields = 0;
76 abort_row();
77
78 Message_builder::start_message(out_buffer, Mysqlx::ServerMessages::RESULTSET_ROW);
79
80 m_row_processing = true;
81 }
82
end_row()83 void Row_builder::end_row()
84 {
85 if (m_row_processing)
86 {
87 Message_builder::end_message();
88
89 m_row_processing = false;
90 }
91 }
92
add_null_field()93 void Row_builder::add_null_field()
94 {
95 ADD_FIELD_HEADER();
96
97 m_out_stream->WriteVarint32(0);
98 }
99
100
add_longlong_field(longlong value,my_bool unsigned_flag)101 void Row_builder::add_longlong_field(longlong value, my_bool unsigned_flag)
102 {
103 ADD_FIELD_HEADER();
104
105 if (unsigned_flag)
106 {
107 m_out_stream->WriteVarint32(CodedOutputStream::VarintSize64(value));
108 m_out_stream->WriteVarint64(value);
109 }
110 else
111 {
112 google::protobuf::uint64 encoded = google::protobuf::internal::WireFormatLite::ZigZagEncode64(value);
113 m_out_stream->WriteVarint32(CodedOutputStream::VarintSize64(encoded));
114 m_out_stream->WriteVarint64(encoded);
115 }
116 }
117
118 /** BEGIN TEMPORARY CODE */
119 typedef decimal_digit_t dec1;
120 typedef longlong dec2;
121 #define DIG_PER_DEC1 9
122 #define DIG_MASK 100000000
123 #define DIG_BASE 1000000000
124 #define DIG_MAX (DIG_BASE-1)
125 #define DIG_BASE2 ((dec2)DIG_BASE * (dec2)DIG_BASE)
126 #define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
127
128
count_leading_zeroes(int i,dec1 val)129 static inline int count_leading_zeroes(int i, dec1 val)
130 {
131 int ret = 0;
132 switch (i)
133 {
134 /* @note Intentional fallthrough in all case labels */
135 case 9: if (val >= 1000000000) break; ++ret; // Fall through.
136 case 8: if (val >= 100000000) break; ++ret; // Fall through.
137 case 7: if (val >= 10000000) break; ++ret; // Fall through.
138 case 6: if (val >= 1000000) break; ++ret; // Fall through.
139 case 5: if (val >= 100000) break; ++ret; // Fall through.
140 case 4: if (val >= 10000) break; ++ret; // Fall through.
141 case 3: if (val >= 1000) break; ++ret; // Fall through.
142 case 2: if (val >= 100) break; ++ret; // Fall through.
143 case 1: if (val >= 10) break; ++ret; // Fall through.
144 case 0: if (val >= 1) break; ++ret; // Fall through.
145 default: { assert(FALSE); }
146 }
147 return ret;
148 }
149
remove_leading_zeroes(const decimal_t * from,int * intg_result)150 static dec1 *remove_leading_zeroes(const decimal_t *from, int *intg_result)
151 {
152 int intg = from->intg, i;
153 dec1 *buf0 = from->buf;
154 i = ((intg - 1) % DIG_PER_DEC1) + 1;
155 while (intg > 0 && *buf0 == 0)
156 {
157 intg -= i;
158 i = DIG_PER_DEC1;
159 buf0++;
160 }
161 if (intg > 0)
162 {
163 intg -= count_leading_zeroes((intg - 1) % DIG_PER_DEC1, *buf0);
164 assert(intg > 0);
165 }
166 else
167 intg = 0;
168 *intg_result = intg;
169 return buf0;
170 }
171
__decimal2string(const decimal_t * from,char * to,int * to_len,int fixed_precision,int fixed_decimals,char filler)172 static int __decimal2string(const decimal_t *from, char *to, int *to_len,
173 int fixed_precision, int fixed_decimals,
174 char filler)
175 {
176 /* {intg_len, frac_len} output widths; {intg, frac} places in input */
177 int len, intg, frac = from->frac, i, intg_len, frac_len, fill;
178 /* number digits before decimal point */
179 int fixed_intg = (fixed_precision ?
180 (fixed_precision - fixed_decimals) : 0);
181 int error = E_DEC_OK;
182 char *s = to;
183 dec1 *buf, *buf0, tmp;
184
185 assert(*to_len >= 2 + from->sign);
186
187 /* removing leading zeroes */
188 buf0 = remove_leading_zeroes(from, &intg);
189 if (unlikely(intg + frac == 0))
190 {
191 intg = 1;
192 tmp = 0;
193 buf0 = &tmp;
194 }
195
196 if (!(intg_len = fixed_precision ? fixed_intg : intg))
197 intg_len = 1;
198 frac_len = fixed_precision ? fixed_decimals : frac;
199 len = from->sign + intg_len + MY_TEST(frac) + frac_len;
200 if (fixed_precision)
201 {
202 if (frac > fixed_decimals)
203 {
204 error = E_DEC_TRUNCATED;
205 frac = fixed_decimals;
206 }
207 if (intg > fixed_intg)
208 {
209 error = E_DEC_OVERFLOW;
210 intg = fixed_intg;
211 }
212 }
213 else if (unlikely(len > --*to_len)) /* reserve one byte for \0 */
214 {
215 int j = len - *to_len; /* excess printable chars */
216 error = (frac && j <= frac + 1) ? E_DEC_TRUNCATED : E_DEC_OVERFLOW;
217
218 /*
219 If we need to cut more places than frac is wide, we'll end up
220 dropping the decimal point as well. Account for this.
221 */
222 if (frac && j >= frac + 1)
223 j--;
224
225 if (j > frac)
226 {
227 intg_len = intg -= j - frac;
228 frac = 0;
229 }
230 else
231 frac -= j;
232 frac_len = frac;
233 len = from->sign + intg_len + MY_TEST(frac) + frac_len;
234 }
235 *to_len = len;
236 s[len] = 0;
237
238 if (from->sign)
239 *s++ = '-';
240
241 if (frac)
242 {
243 char *s1 = s + intg_len;
244 fill = frac_len - frac;
245 buf = buf0 + ROUND_UP(intg);
246 *s1++ = '.';
247 for (; frac>0; frac -= DIG_PER_DEC1)
248 {
249 dec1 x = *buf++;
250 for (i = MY_MIN(frac, DIG_PER_DEC1); i; i--)
251 {
252 dec1 y = x / DIG_MASK;
253 *s1++ = '0' + (uchar)y;
254 x -= y*DIG_MASK;
255 x *= 10;
256 }
257 }
258 for (; fill > 0; fill--)
259 *s1++ = filler;
260 }
261
262 fill = intg_len - intg;
263 if (intg == 0)
264 fill--; /* symbol 0 before digital point */
265 for (; fill > 0; fill--)
266 *s++ = filler;
267 if (intg)
268 {
269 s += intg;
270 for (buf = buf0 + ROUND_UP(intg); intg>0; intg -= DIG_PER_DEC1)
271 {
272 dec1 x = *--buf;
273 for (i = MY_MIN(intg, DIG_PER_DEC1); i; i--)
274 {
275 dec1 y = x / 10;
276 *--s = '0' + (uchar)(x - y * 10);
277 x = y;
278 }
279 }
280 }
281 else
282 *s = '0';
283
284 return error;
285 }
286 /* END TEMPORARY CODE */
287
288
add_decimal_field(const decimal_t * value)289 void Row_builder::add_decimal_field(const decimal_t * value)
290 {
291 ADD_FIELD_HEADER();
292
293 /*TODO: inefficient, refactor to skip the string conversion*/
294 std::string str_buf;
295 int str_len = 200;
296 str_buf.resize(str_len);
297 __decimal2string(value, &(str_buf)[0], &str_len, 0, 0, 0);
298 str_buf.resize(str_len);
299
300 mysqlx::Decimal dec(str_buf);
301 std::string dec_bytes = dec.to_bytes();
302
303 m_out_stream->WriteVarint32(static_cast<google::protobuf::uint32>(dec_bytes.length()));
304 m_out_stream->WriteString(dec_bytes);
305 }
306
add_decimal_field(const char * const value,size_t length)307 void Row_builder::add_decimal_field(const char * const value, size_t length)
308 {
309 ADD_FIELD_HEADER();
310
311 std::string dec_str(value, length);
312 mysqlx::Decimal dec(dec_str);
313 std::string dec_bytes = dec.to_bytes();
314
315 m_out_stream->WriteVarint32(static_cast<google::protobuf::uint32>(dec_bytes.length()));
316 m_out_stream->WriteString(dec_bytes);
317 }
318
add_double_field(double value)319 void Row_builder::add_double_field(double value)
320 {
321 ADD_FIELD_HEADER();
322
323 m_out_stream->WriteVarint32(sizeof(google::protobuf::uint64));
324 m_out_stream->WriteLittleEndian64(google::protobuf::internal::WireFormatLite::EncodeDouble(value));
325 }
326
add_float_field(float value)327 void Row_builder::add_float_field(float value)
328 {
329 ADD_FIELD_HEADER();
330
331 m_out_stream->WriteVarint32(sizeof(google::protobuf::uint32));
332 m_out_stream->WriteLittleEndian32(google::protobuf::internal::WireFormatLite::EncodeFloat(value));
333 }
334
add_date_field(const MYSQL_TIME * value)335 void Row_builder::add_date_field(const MYSQL_TIME * value)
336 {
337 ADD_FIELD_HEADER();
338
339 google::protobuf::uint32 size = CodedOutputStream::VarintSize64(value->year)
340 + CodedOutputStream::VarintSize64(value->month)
341 + CodedOutputStream::VarintSize64(value->day);
342
343 m_out_stream->WriteVarint32(size);
344
345 m_out_stream->WriteVarint64(value->year);
346 m_out_stream->WriteVarint64(value->month);
347 m_out_stream->WriteVarint64(value->day);
348 }
349
get_time_size(const MYSQL_TIME * value)350 size_t Row_builder::get_time_size(const MYSQL_TIME * value)
351 {
352 size_t result = 0;
353 if (value->hour != 0 || value->minute != 0 || value->second != 0 || value->second_part != 0)
354 {
355 result += CodedOutputStream::VarintSize64(value->hour);
356 }
357 if (value->minute != 0 || value->second != 0 || value->second_part != 0)
358 {
359 result += CodedOutputStream::VarintSize64(value->minute);
360 }
361 if (value->second != 0 || value->second_part != 0)
362 {
363 result += CodedOutputStream::VarintSize64(value->second);
364 }
365 if (value->second_part != 0)
366 {
367 result += CodedOutputStream::VarintSize64(value->second_part);
368 }
369
370 return result;
371 }
372
append_time_values(const MYSQL_TIME * value,CodedOutputStream * out_stream)373 void Row_builder::append_time_values(const MYSQL_TIME * value, CodedOutputStream* out_stream)
374 {
375 // optimize the output size skipping the right-most 0's
376 if (value->hour != 0 || value->minute != 0 || value->second != 0 || value->second_part != 0)
377 {
378 out_stream->WriteVarint64(value->hour);
379 }
380 if (value->minute != 0 || value->second != 0 || value->second_part != 0)
381 {
382 out_stream->WriteVarint64(value->minute);
383 }
384 if (value->second != 0 || value->second_part != 0)
385 {
386 out_stream->WriteVarint64(value->second);
387 }
388 if (value->second_part != 0)
389 {
390 out_stream->WriteVarint64(value->second_part);
391 }
392 }
393
add_time_field(const MYSQL_TIME * value,uint decimals)394 void Row_builder::add_time_field(const MYSQL_TIME * value, uint decimals)
395 {
396 ADD_FIELD_HEADER();
397
398 m_out_stream->WriteVarint32(static_cast<google::protobuf::uint32>(get_time_size(value) + 1)); // +1 for sign
399
400 /*sign*/
401 google::protobuf::uint8 neg = (value->neg) ? 0x01 : 0x00;
402 m_out_stream->WriteRaw(&neg, 1);
403
404 append_time_values(value, m_out_stream.get());
405 }
406
add_datetime_field(const MYSQL_TIME * value,uint decimals)407 void Row_builder::add_datetime_field(const MYSQL_TIME * value, uint decimals)
408 {
409 ADD_FIELD_HEADER();
410
411 google::protobuf::uint32 size = CodedOutputStream::VarintSize64(value->year)
412 + CodedOutputStream::VarintSize64(value->month)
413 + CodedOutputStream::VarintSize64(value->day)
414 + static_cast<int>(get_time_size(value));
415
416 m_out_stream->WriteVarint32(size);
417
418 m_out_stream->WriteVarint64(value->year);
419 m_out_stream->WriteVarint64(value->month);
420 m_out_stream->WriteVarint64(value->day);
421
422 append_time_values(value, m_out_stream.get());
423 }
424
add_string_field(const char * const value,size_t length,const CHARSET_INFO * const valuecs)425 void Row_builder::add_string_field(const char * const value, size_t length,
426 const CHARSET_INFO * const valuecs)
427 {
428 ADD_FIELD_HEADER();
429
430 m_out_stream->WriteVarint32(static_cast<google::protobuf::uint32>(length + 1)); // 1 byte for thre trailing '\0'
431
432 m_out_stream->WriteRaw(value, static_cast<int>(length));
433 char zero = '\0';
434 m_out_stream->WriteRaw(&zero, 1);
435 }
436
add_set_field(const char * const value,size_t length,const CHARSET_INFO * const valuecs)437 void Row_builder::add_set_field(const char * const value, size_t length,
438 const CHARSET_INFO * const valuecs)
439 {
440 ADD_FIELD_HEADER();
441
442 // special case: empty SET
443 if (0 == length)
444 {
445 // write length=0x01 to the buffer and we're done here
446 m_out_stream->WriteVarint32(1);
447 m_out_stream->WriteVarint64(0x01);
448 return;
449 }
450
451 std::vector<std::string> set_vals;
452 const char *comma, *p_value = value;
453 unsigned int elem_len;
454 do
455 {
456 comma = std::strchr(p_value, ',');
457 if (comma != NULL)
458 {
459 elem_len = static_cast<unsigned int>(comma - p_value);
460 set_vals.push_back(std::string(p_value, elem_len));
461 p_value = comma + 1;
462 }
463 } while (comma != NULL);
464
465 // still sth left to store
466 if ((size_t)(p_value - value) < length)
467 {
468 elem_len = static_cast<unsigned int>(length - (p_value - value));
469 set_vals.push_back(std::string(p_value, elem_len));
470 }
471
472 // calculate size needed for all lengths and values
473 google::protobuf::uint32 size = 0;
474 for (size_t i = 0; i < set_vals.size(); ++i)
475 {
476 size += CodedOutputStream::VarintSize64(set_vals[i].length());
477 size += static_cast<google::protobuf::uint32>(set_vals[i].length());
478 }
479
480 // write total size to the buffer
481 m_out_stream->WriteVarint32(size);
482
483 // write all lengths and values to the buffer
484 for (size_t i = 0; i < set_vals.size(); ++i)
485 {
486 m_out_stream->WriteVarint64(set_vals[i].length());
487 m_out_stream->WriteString(set_vals[i]);
488 }
489 }
490
add_bit_field(const char * const value,size_t length,const CHARSET_INFO * const valuecs)491 void Row_builder::add_bit_field(const char * const value, size_t length,
492 const CHARSET_INFO * const valuecs)
493 {
494 ADD_FIELD_HEADER();
495 assert(length <= 8);
496
497 google::protobuf::uint64 binary_value = 0;
498 for (size_t i = 0; i < length; ++i)
499 {
500 binary_value += ((static_cast<google::protobuf::uint64>(value[i]) & 0xff) << ((length - i - 1) * 8));
501 }
502 m_out_stream->WriteVarint32(CodedOutputStream::VarintSize64(binary_value));
503 m_out_stream->WriteVarint64(binary_value);
504 }
505