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