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