1 /* Copyright (C) 2014 SkySQL Ab, MariaDB Corporation Ab
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), "%lg", 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 
Json_writer_temp_disable(THD * thd_arg)297 Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg)
298 {
299   thd= thd_arg;
300   thd->opt_trace.disable_tracing_if_required();
301 }
~Json_writer_temp_disable()302 Json_writer_temp_disable::~Json_writer_temp_disable()
303 {
304   thd->opt_trace.enable_tracing_if_required();
305 }
306 
on_add_member(const char * name,size_t len)307 bool Single_line_formatting_helper::on_add_member(const char *name,
308                                                   size_t len)
309 {
310   DBUG_ASSERT(state== INACTIVE || state == DISABLED);
311   if (state != DISABLED)
312   {
313     // remove everything from the array
314     buf_ptr= buffer;
315 
316     //append member name to the array
317     if (len < MAX_LINE_LEN)
318     {
319       memcpy(buf_ptr, name, len);
320       buf_ptr+=len;
321       *(buf_ptr++)= 0;
322 
323       line_len= owner->indent_level + (uint)len + 1;
324       state= ADD_MEMBER;
325       return true; // handled
326     }
327   }
328   return false; // not handled
329 }
330 
331 
on_start_array()332 bool Single_line_formatting_helper::on_start_array()
333 {
334   if (state == ADD_MEMBER)
335   {
336     state= IN_ARRAY;
337     return true; // handled
338   }
339   else
340   {
341     if (state != DISABLED)
342       state= INACTIVE;
343     // TODO: what if we have accumulated some stuff already? shouldn't we
344     // flush it?
345     return false; // not handled
346   }
347 }
348 
349 
on_end_array()350 bool Single_line_formatting_helper::on_end_array()
351 {
352   if (state == IN_ARRAY)
353   {
354     flush_on_one_line();
355     state= INACTIVE;
356     return true; // handled
357   }
358   return false; // not handled
359 }
360 
361 
on_start_object()362 void Single_line_formatting_helper::on_start_object()
363 {
364   // Nested objects will not be printed on one line
365   disable_and_flush();
366 }
367 
368 
on_add_str(const char * str,size_t len)369 bool Single_line_formatting_helper::on_add_str(const char *str,
370                                                size_t len)
371 {
372   if (state == IN_ARRAY)
373   {
374     // New length will be:
375     //  "$string",
376     //  quote + quote + comma + space = 4
377     if (line_len + len + 4 > MAX_LINE_LEN)
378     {
379       disable_and_flush();
380       return false; // didn't handle the last element
381     }
382 
383     //append string to array
384     memcpy(buf_ptr, str, len);
385     buf_ptr+=len;
386     *(buf_ptr++)= 0;
387     line_len += (uint)len + 4;
388     return true; // handled
389   }
390 
391   disable_and_flush();
392   return false; // not handled
393 }
394 
395 
396 /*
397   Append everything accumulated to the output on one line
398 */
399 
flush_on_one_line()400 void Single_line_formatting_helper::flush_on_one_line()
401 {
402   owner->start_sub_element();
403   char *ptr= buffer;
404   int nr= 0;
405   while (ptr < buf_ptr)
406   {
407     char *str= ptr;
408 
409     if (nr == 0)
410     {
411       owner->output.append('"');
412       owner->output.append(str);
413       owner->output.append("\": ");
414       owner->output.append('[');
415     }
416     else
417     {
418       if (nr != 1)
419         owner->output.append(", ");
420       owner->output.append('"');
421       owner->output.append(str);
422       owner->output.append('"');
423     }
424     nr++;
425 
426     while (*ptr!=0)
427       ptr++;
428     ptr++;
429   }
430   owner->output.append(']');
431   /* We've printed out the contents of the buffer, mark it as empty */
432   buf_ptr= buffer;
433 }
434 
435 
disable_and_flush()436 void Single_line_formatting_helper::disable_and_flush()
437 {
438   if (state == DISABLED)
439     return;
440 
441   bool start_array= (state == IN_ARRAY);
442   state= DISABLED;
443   // deactivate ourselves and flush all accumulated calls.
444   char *ptr= buffer;
445   int nr= 0;
446   while (ptr < buf_ptr)
447   {
448     char *str= ptr;
449     size_t len= strlen(str);
450 
451     if (nr == 0)
452     {
453       owner->add_member(str, len);
454       if (start_array)
455         owner->start_array();
456     }
457     else
458     {
459       //if (nr == 1)
460       //  owner->start_array();
461       owner->add_str(str, len);
462     }
463 
464     nr++;
465     ptr+= len+1;
466   }
467   buf_ptr= buffer;
468   state= INACTIVE;
469 }
470 
471