summaryrefslogtreecommitdiffstats
path: root/sql/my_json_writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/my_json_writer.cc')
-rw-r--r--sql/my_json_writer.cc496
1 files changed, 496 insertions, 0 deletions
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
new file mode 100644
index 00000000..54eb8423
--- /dev/null
+++ b/sql/my_json_writer.cc
@@ -0,0 +1,496 @@
+/* Copyright (C) 2014, 2021, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include "my_global.h"
+#include "my_json_writer.h"
+
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+
+bool Json_writer::named_item_expected() const
+{
+ return named_items_expectation.size()
+ && named_items_expectation.back();
+}
+#endif
+
+void Json_writer::append_indent()
+{
+ if (!document_start)
+ output.append('\n');
+ for (int i=0; i< indent_level; i++)
+ output.append(' ');
+}
+
+inline void Json_writer::on_start_object()
+{
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ if(!fmt_helper.is_making_writer_calls())
+ {
+ if (got_name != named_item_expected())
+ {
+ sql_print_error(got_name
+ ? "Json_writer got a member name which is not expected.\n"
+ : "Json_writer: a member name was expected.\n");
+ VALIDITY_ASSERT(got_name == named_item_expected());
+ }
+ named_items_expectation.push_back(true);
+ }
+#endif
+ fmt_helper.on_start_object();
+}
+
+void Json_writer::start_object()
+{
+ on_start_object();
+
+ if (!element_started)
+ start_element();
+
+ output.append('{');
+ indent_level+=INDENT_SIZE;
+ first_child=true;
+ element_started= false;
+ document_start= false;
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ got_name= false;
+ named_items.emplace();
+#endif
+}
+
+void Json_writer::start_array()
+{
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ if(!fmt_helper.is_making_writer_calls())
+ {
+ VALIDITY_ASSERT(got_name == named_item_expected());
+ named_items_expectation.push_back(false);
+ got_name= false;
+ if (document_start)
+ named_items.emplace();
+ }
+#endif
+
+ if (fmt_helper.on_start_array())
+ return;
+
+ if (!element_started)
+ start_element();
+
+ output.append('[');
+ indent_level+=INDENT_SIZE;
+ first_child=true;
+ element_started= false;
+ document_start= false;
+}
+
+
+void Json_writer::end_object()
+{
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ VALIDITY_ASSERT(named_item_expected());
+ named_items_expectation.pop_back();
+ VALIDITY_ASSERT(!got_name);
+ got_name= false;
+ VALIDITY_ASSERT(named_items.size());
+ named_items.pop();
+#endif
+ indent_level-=INDENT_SIZE;
+ if (!first_child)
+ append_indent();
+ first_child= false;
+ output.append('}');
+}
+
+
+void Json_writer::end_array()
+{
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ VALIDITY_ASSERT(!named_item_expected());
+ named_items_expectation.pop_back();
+ got_name= false;
+#endif
+ if (fmt_helper.on_end_array())
+ return;
+ indent_level-=INDENT_SIZE;
+ if (!first_child)
+ append_indent();
+ output.append(']');
+}
+
+
+Json_writer& Json_writer::add_member(const char *name)
+{
+ size_t len= strlen(name);
+ return add_member(name, len);
+}
+
+Json_writer& Json_writer::add_member(const char *name, size_t len)
+{
+ if (!fmt_helper.on_add_member(name, len))
+ {
+ // assert that we are in an object
+ DBUG_ASSERT(!element_started);
+ start_element();
+
+ output.append('"');
+ output.append(name, len);
+ output.append(STRING_WITH_LEN("\": "));
+ }
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ if (!fmt_helper.is_making_writer_calls())
+ {
+ VALIDITY_ASSERT(!got_name);
+ got_name= true;
+ VALIDITY_ASSERT(named_items.size());
+ auto& named_items_keys= named_items.top();
+ auto emplaced= named_items_keys.emplace(name, len);
+ auto is_uniq_key= emplaced.second;
+ if(!is_uniq_key)
+ {
+ sql_print_error("Duplicated key: %s\n", emplaced.first->c_str());
+ VALIDITY_ASSERT(is_uniq_key);
+ }
+ }
+#endif
+ return *this;
+}
+
+
+/*
+ Used by formatting helper to print something that is formatted by the helper.
+ We should only separate it from the previous element.
+*/
+
+void Json_writer::start_sub_element()
+{
+ //element_started= true;
+ if (first_child)
+ first_child= false;
+ else
+ output.append(',');
+
+ append_indent();
+}
+
+
+void Json_writer::start_element()
+{
+ element_started= true;
+
+ if (first_child)
+ first_child= false;
+ else
+ output.append(',');
+
+ append_indent();
+}
+
+void Json_writer::add_ll(longlong val)
+{
+ char buf[64];
+ my_snprintf(buf, sizeof(buf), "%lld", val);
+ add_unquoted_str(buf);
+}
+
+void Json_writer::add_ull(ulonglong val)
+{
+ char buf[64];
+ my_snprintf(buf, sizeof(buf), "%llu", val);
+ add_unquoted_str(buf);
+}
+
+
+/* Add a memory size, printing in Kb, Kb, Gb if necessary */
+void Json_writer::add_size(longlong val)
+{
+ char buf[64];
+ size_t len;
+ if (val < 1024)
+ len= my_snprintf(buf, sizeof(buf), "%lld", val);
+ else if (val < 1024*1024*16)
+ {
+ /* Values less than 16MB are specified in KB for precision */
+ len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
+ strcpy(buf + len, "Kb");
+ len+= 2;
+ }
+ else
+ {
+ len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
+ strcpy(buf + len, "Mb");
+ len+= 2;
+ }
+ add_str(buf, len);
+}
+
+
+void Json_writer::add_double(double val)
+{
+ char buf[64];
+ size_t len= my_snprintf(buf, sizeof(buf), "%-.11lg", val);
+ add_unquoted_str(buf, len);
+}
+
+
+void Json_writer::add_bool(bool val)
+{
+ add_unquoted_str(val? "true" : "false");
+}
+
+
+void Json_writer::add_null()
+{
+ add_unquoted_str("null", (size_t) 4);
+}
+
+
+void Json_writer::add_unquoted_str(const char* str)
+{
+ size_t len= strlen(str);
+ add_unquoted_str(str, len);
+}
+
+void Json_writer::add_unquoted_str(const char* str, size_t len)
+{
+ VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
+ got_name == named_item_expected());
+ if (on_add_str(str, len))
+ return;
+
+ if (!element_started)
+ start_element();
+
+ output.append(str, len);
+ element_started= false;
+}
+
+inline bool Json_writer::on_add_str(const char *str, size_t num_bytes)
+{
+#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
+ got_name= false;
+#endif
+ bool helped= fmt_helper.on_add_str(str, num_bytes);
+ return helped;
+}
+
+void Json_writer::add_str(const char *str)
+{
+ size_t len= strlen(str);
+ add_str(str, len);
+}
+
+/*
+ This function is used to add only num_bytes of str to the output string
+*/
+
+void Json_writer::add_str(const char* str, size_t num_bytes)
+{
+ VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
+ got_name == named_item_expected());
+ if (on_add_str(str, num_bytes))
+ return;
+
+ if (!element_started)
+ start_element();
+
+ output.append('"');
+ output.append(str, num_bytes);
+ output.append('"');
+ element_started= false;
+}
+
+void Json_writer::add_str(const String &str)
+{
+ add_str(str.ptr(), str.length());
+}
+
+#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
+thread_local std::vector<bool> Json_writer_struct::named_items_expectation;
+#endif
+
+Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg)
+{
+ thd= thd_arg;
+ thd->opt_trace.disable_tracing_if_required();
+}
+Json_writer_temp_disable::~Json_writer_temp_disable()
+{
+ thd->opt_trace.enable_tracing_if_required();
+}
+
+bool Single_line_formatting_helper::on_add_member(const char *name,
+ size_t len)
+{
+ DBUG_ASSERT(state== INACTIVE || state == DISABLED);
+ if (state != DISABLED)
+ {
+ // remove everything from the array
+ buf_ptr= buffer;
+
+ //append member name to the array
+ if (len < MAX_LINE_LEN)
+ {
+ memcpy(buf_ptr, name, len);
+ buf_ptr+=len;
+ *(buf_ptr++)= 0;
+
+ line_len= owner->indent_level + (uint)len + 1;
+ state= ADD_MEMBER;
+ return true; // handled
+ }
+ }
+ return false; // not handled
+}
+
+
+bool Single_line_formatting_helper::on_start_array()
+{
+ if (state == ADD_MEMBER)
+ {
+ state= IN_ARRAY;
+ return true; // handled
+ }
+ else
+ {
+ if (state != DISABLED)
+ state= INACTIVE;
+ // TODO: what if we have accumulated some stuff already? shouldn't we
+ // flush it?
+ return false; // not handled
+ }
+}
+
+
+bool Single_line_formatting_helper::on_end_array()
+{
+ if (state == IN_ARRAY)
+ {
+ flush_on_one_line();
+ state= INACTIVE;
+ return true; // handled
+ }
+ return false; // not handled
+}
+
+
+void Single_line_formatting_helper::on_start_object()
+{
+ // Nested objects will not be printed on one line
+ disable_and_flush();
+}
+
+
+bool Single_line_formatting_helper::on_add_str(const char *str,
+ size_t len)
+{
+ if (state == IN_ARRAY)
+ {
+ // New length will be:
+ // "$string",
+ // quote + quote + comma + space = 4
+ if (line_len + len + 4 > MAX_LINE_LEN)
+ {
+ disable_and_flush();
+ return false; // didn't handle the last element
+ }
+
+ //append string to array
+ memcpy(buf_ptr, str, len);
+ buf_ptr+=len;
+ *(buf_ptr++)= 0;
+ line_len += (uint)len + 4;
+ return true; // handled
+ }
+
+ disable_and_flush();
+ return false; // not handled
+}
+
+
+/*
+ Append everything accumulated to the output on one line
+*/
+
+void Single_line_formatting_helper::flush_on_one_line()
+{
+ owner->start_sub_element();
+ char *ptr= buffer;
+ int nr= 0;
+ while (ptr < buf_ptr)
+ {
+ char *str= ptr;
+
+ if (nr == 0)
+ {
+ owner->output.append('"');
+ owner->output.append(str);
+ owner->output.append(STRING_WITH_LEN("\": "));
+ owner->output.append('[');
+ }
+ else
+ {
+ if (nr != 1)
+ owner->output.append(STRING_WITH_LEN(", "));
+ owner->output.append('"');
+ owner->output.append(str);
+ owner->output.append('"');
+ }
+ nr++;
+
+ while (*ptr!=0)
+ ptr++;
+ ptr++;
+ }
+ owner->output.append(']');
+ /* We've printed out the contents of the buffer, mark it as empty */
+ buf_ptr= buffer;
+}
+
+
+void Single_line_formatting_helper::disable_and_flush()
+{
+ if (state == DISABLED)
+ return;
+
+ bool start_array= (state == IN_ARRAY);
+ state= DISABLED;
+ // deactivate ourselves and flush all accumulated calls.
+ char *ptr= buffer;
+ int nr= 0;
+ while (ptr < buf_ptr)
+ {
+ char *str= ptr;
+ size_t len= strlen(str);
+
+ if (nr == 0)
+ {
+ owner->add_member(str, len);
+ if (start_array)
+ owner->start_array();
+ }
+ else
+ {
+ //if (nr == 1)
+ // owner->start_array();
+ owner->add_str(str, len);
+ }
+
+ nr++;
+ ptr+= len+1;
+ }
+ buf_ptr= buffer;
+ state= INACTIVE;
+}
+