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