1 /* Copyright (C) 2014, 2020, MariaDB Corporation.
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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 #include "mariadb.h"
17 #include "sql_priv.h"
18 #include "sql_string.h"
19 #include "my_json_writer.h"
20 
21 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
named_item_expected() const22 bool Json_writer::named_item_expected() const
23 {
24   return named_items_expectation.size()
25       && named_items_expectation.back();
26 }
27 #endif
28 
append_indent()29 void Json_writer::append_indent()
30 {
31   if (!document_start)
32     output.append('\n');
33   for (int i=0; i< indent_level; i++)
34     output.append(' ');
35 }
36 
on_start_object()37 inline void Json_writer::on_start_object()
38 {
39 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
40   if(!fmt_helper.is_making_writer_calls())
41   {
42     VALIDITY_ASSERT(got_name == named_item_expected());
43     named_items_expectation.push_back(true);
44   }
45 #endif
46   fmt_helper.on_start_object();
47 }
48 
start_object()49 void Json_writer::start_object()
50 {
51   on_start_object();
52 
53   if (!element_started)
54     start_element();
55 
56   output.append("{");
57   indent_level+=INDENT_SIZE;
58   first_child=true;
59   element_started= false;
60   document_start= false;
61 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
62   got_name= false;
63 #endif
64 }
65 
start_array()66 void Json_writer::start_array()
67 {
68 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
69   if(!fmt_helper.is_making_writer_calls())
70   {
71     VALIDITY_ASSERT(got_name == named_item_expected());
72     named_items_expectation.push_back(false);
73     got_name= false;
74   }
75 #endif
76 
77   if (fmt_helper.on_start_array())
78     return;
79 
80   if (!element_started)
81     start_element();
82 
83   output.append("[");
84   indent_level+=INDENT_SIZE;
85   first_child=true;
86   element_started= false;
87   document_start= false;
88 }
89 
90 
end_object()91 void Json_writer::end_object()
92 {
93 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
94   VALIDITY_ASSERT(named_item_expected());
95   named_items_expectation.pop_back();
96   VALIDITY_ASSERT(!got_name);
97   got_name= false;
98 #endif
99   indent_level-=INDENT_SIZE;
100   if (!first_child)
101     append_indent();
102   first_child= false;
103   output.append("}");
104 }
105 
106 
end_array()107 void Json_writer::end_array()
108 {
109 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
110   VALIDITY_ASSERT(!named_item_expected());
111   named_items_expectation.pop_back();
112   got_name= false;
113 #endif
114   if (fmt_helper.on_end_array())
115     return;
116   indent_level-=INDENT_SIZE;
117   if (!first_child)
118     append_indent();
119   output.append("]");
120 }
121 
122 
add_member(const char * name)123 Json_writer& Json_writer::add_member(const char *name)
124 {
125   size_t len= strlen(name);
126   return add_member(name, len);
127 }
128 
add_member(const char * name,size_t len)129 Json_writer& Json_writer::add_member(const char *name, size_t len)
130 {
131   if (!fmt_helper.on_add_member(name, len))
132   {
133     // assert that we are in an object
134     DBUG_ASSERT(!element_started);
135     start_element();
136 
137     output.append('"');
138     output.append(name, len);
139     output.append("\": ", 3);
140   }
141 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
142   if (!fmt_helper.is_making_writer_calls())
143     got_name= true;
144 #endif
145   return *this;
146 }
147 
148 
149 /*
150   Used by formatting helper to print something that is formatted by the helper.
151   We should only separate it from the previous element.
152 */
153 
start_sub_element()154 void Json_writer::start_sub_element()
155 {
156   //element_started= true;
157   if (first_child)
158     first_child= false;
159   else
160     output.append(',');
161 
162   append_indent();
163 }
164 
165 
start_element()166 void Json_writer::start_element()
167 {
168   element_started= true;
169 
170   if (first_child)
171     first_child= false;
172   else
173     output.append(',');
174 
175   append_indent();
176 }
177 
add_ll(longlong val)178 void Json_writer::add_ll(longlong val)
179 {
180   char buf[64];
181   my_snprintf(buf, sizeof(buf), "%lld", val);
182   add_unquoted_str(buf);
183 }
184 
add_ull(ulonglong val)185 void Json_writer::add_ull(ulonglong val)
186 {
187   char buf[64];
188   my_snprintf(buf, sizeof(buf), "%llu", val);
189   add_unquoted_str(buf);
190 }
191 
192 
193 /* Add a memory size, printing in Kb, Kb, Gb if necessary */
add_size(longlong val)194 void Json_writer::add_size(longlong val)
195 {
196   char buf[64];
197   size_t len;
198   if (val < 1024)
199     len= my_snprintf(buf, sizeof(buf), "%lld", val);
200   else if (val < 1024*1024*16)
201   {
202     /* Values less than 16MB are specified in KB for precision */
203     len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
204     strcpy(buf + len, "Kb");
205     len+= 2;
206   }
207   else
208   {
209     len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
210     strcpy(buf + len, "Mb");
211     len+= 2;
212   }
213   add_str(buf, len);
214 }
215 
216 
add_double(double val)217 void Json_writer::add_double(double val)
218 {
219   char buf[64];
220   size_t len= my_snprintf(buf, sizeof(buf), "%-.11lg", val);
221   add_unquoted_str(buf, len);
222 }
223 
224 
add_bool(bool val)225 void Json_writer::add_bool(bool val)
226 {
227   add_unquoted_str(val? "true" : "false");
228 }
229 
230 
add_null()231 void Json_writer::add_null()
232 {
233   add_unquoted_str("null", (size_t) 4);
234 }
235 
236 
add_unquoted_str(const char * str)237 void Json_writer::add_unquoted_str(const char* str)
238 {
239   size_t len= strlen(str);
240   add_unquoted_str(str, len);
241 }
242 
add_unquoted_str(const char * str,size_t len)243 void Json_writer::add_unquoted_str(const char* str, size_t len)
244 {
245   VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
246                   got_name == named_item_expected());
247   if (on_add_str(str, len))
248     return;
249 
250   if (!element_started)
251     start_element();
252 
253   output.append(str, len);
254   element_started= false;
255 }
256 
on_add_str(const char * str,size_t num_bytes)257 inline bool Json_writer::on_add_str(const char *str, size_t num_bytes)
258 {
259 #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
260   got_name= false;
261 #endif
262   bool helped= fmt_helper.on_add_str(str, num_bytes);
263   return helped;
264 }
265 
add_str(const char * str)266 void Json_writer::add_str(const char *str)
267 {
268   size_t len= strlen(str);
269   add_str(str, len);
270 }
271 
272 /*
273   This function is used to add only num_bytes of str to the output string
274 */
275 
add_str(const char * str,size_t num_bytes)276 void Json_writer::add_str(const char* str, size_t num_bytes)
277 {
278   VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
279                   got_name == named_item_expected());
280   if (on_add_str(str, num_bytes))
281     return;
282 
283   if (!element_started)
284     start_element();
285 
286   output.append('"');
287   output.append(str, num_bytes);
288   output.append('"');
289   element_started= false;
290 }
291 
add_str(const String & str)292 void Json_writer::add_str(const String &str)
293 {
294   add_str(str.ptr(), str.length());
295 }
296 
297 #ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
298 thread_local std::vector<bool> Json_writer_struct::named_items_expectation;
299 #endif
300 
Json_writer_temp_disable(THD * thd_arg)301 Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg)
302 {
303   thd= thd_arg;
304   thd->opt_trace.disable_tracing_if_required();
305 }
~Json_writer_temp_disable()306 Json_writer_temp_disable::~Json_writer_temp_disable()
307 {
308   thd->opt_trace.enable_tracing_if_required();
309 }
310 
on_add_member(const char * name,size_t len)311 bool Single_line_formatting_helper::on_add_member(const char *name,
312                                                   size_t len)
313 {
314   DBUG_ASSERT(state== INACTIVE || state == DISABLED);
315   if (state != DISABLED)
316   {
317     // remove everything from the array
318     buf_ptr= buffer;
319 
320     //append member name to the array
321     if (len < MAX_LINE_LEN)
322     {
323       memcpy(buf_ptr, name, len);
324       buf_ptr+=len;
325       *(buf_ptr++)= 0;
326 
327       line_len= owner->indent_level + (uint)len + 1;
328       state= ADD_MEMBER;
329       return true; // handled
330     }
331   }
332   return false; // not handled
333 }
334 
335 
on_start_array()336 bool Single_line_formatting_helper::on_start_array()
337 {
338   if (state == ADD_MEMBER)
339   {
340     state= IN_ARRAY;
341     return true; // handled
342   }
343   else
344   {
345     if (state != DISABLED)
346       state= INACTIVE;
347     // TODO: what if we have accumulated some stuff already? shouldn't we
348     // flush it?
349     return false; // not handled
350   }
351 }
352 
353 
on_end_array()354 bool Single_line_formatting_helper::on_end_array()
355 {
356   if (state == IN_ARRAY)
357   {
358     flush_on_one_line();
359     state= INACTIVE;
360     return true; // handled
361   }
362   return false; // not handled
363 }
364 
365 
on_start_object()366 void Single_line_formatting_helper::on_start_object()
367 {
368   // Nested objects will not be printed on one line
369   disable_and_flush();
370 }
371 
372 
on_add_str(const char * str,size_t len)373 bool Single_line_formatting_helper::on_add_str(const char *str,
374                                                size_t len)
375 {
376   if (state == IN_ARRAY)
377   {
378     // New length will be:
379     //  "$string",
380     //  quote + quote + comma + space = 4
381     if (line_len + len + 4 > MAX_LINE_LEN)
382     {
383       disable_and_flush();
384       return false; // didn't handle the last element
385     }
386 
387     //append string to array
388     memcpy(buf_ptr, str, len);
389     buf_ptr+=len;
390     *(buf_ptr++)= 0;
391     line_len += (uint)len + 4;
392     return true; // handled
393   }
394 
395   disable_and_flush();
396   return false; // not handled
397 }
398 
399 
400 /*
401   Append everything accumulated to the output on one line
402 */
403 
flush_on_one_line()404 void Single_line_formatting_helper::flush_on_one_line()
405 {
406   owner->start_sub_element();
407   char *ptr= buffer;
408   int nr= 0;
409   while (ptr < buf_ptr)
410   {
411     char *str= ptr;
412 
413     if (nr == 0)
414     {
415       owner->output.append('"');
416       owner->output.append(str);
417       owner->output.append("\": ");
418       owner->output.append('[');
419     }
420     else
421     {
422       if (nr != 1)
423         owner->output.append(", ");
424       owner->output.append('"');
425       owner->output.append(str);
426       owner->output.append('"');
427     }
428     nr++;
429 
430     while (*ptr!=0)
431       ptr++;
432     ptr++;
433   }
434   owner->output.append(']');
435   /* We've printed out the contents of the buffer, mark it as empty */
436   buf_ptr= buffer;
437 }
438 
439 
disable_and_flush()440 void Single_line_formatting_helper::disable_and_flush()
441 {
442   if (state == DISABLED)
443     return;
444 
445   bool start_array= (state == IN_ARRAY);
446   state= DISABLED;
447   // deactivate ourselves and flush all accumulated calls.
448   char *ptr= buffer;
449   int nr= 0;
450   while (ptr < buf_ptr)
451   {
452     char *str= ptr;
453     size_t len= strlen(str);
454 
455     if (nr == 0)
456     {
457       owner->add_member(str, len);
458       if (start_array)
459         owner->start_array();
460     }
461     else
462     {
463       //if (nr == 1)
464       //  owner->start_array();
465       owner->add_str(str, len);
466     }
467 
468     nr++;
469     ptr+= len+1;
470   }
471   buf_ptr= buffer;
472   state= INACTIVE;
473 }
474 
475