From f27cb85eb220577cc073becd5d4a4ccb882b9892 Mon Sep 17 00:00:00 2001 From: Annirudh Prasad Date: Sun, 16 May 2021 19:18:13 -0700 Subject: [PATCH] Backport Bug#28949700: SUBOPTIMAL USE OF STRING::RESERVE() IN WRAPPER_TO_STRING() to 5.7 --- sql/json_dom.cc | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/sql/json_dom.cc b/sql/json_dom.cc index 9b502ab53948..9b463158496f 100644 --- a/sql/json_dom.cc +++ b/sql/json_dom.cc @@ -1570,6 +1570,16 @@ void Json_array::clear() delete_container_pointers(m_v); } +/** + Reserve space in a string buffer. If reallocation is needed, + increase the size of the buffer exponentially. + @param buffer the string buffer + @param needed the number of bytes needed + @return true on error, false on success +*/ +static bool reserve(String *buffer, size_t needed) { + return buffer->reserve(needed, buffer->length()); +} /** Perform quoting on a JSON string to make an external representation @@ -1605,8 +1615,8 @@ void Json_array::clear() */ bool double_quote(const char *cptr, size_t length, String *buf) { - if (buf->append('"')) - return true; /* purecov: inspected */ + if (reserve(buf, length + 2) || buf->append('"')) + return true; /* purecov: inspected */ for (size_t i= 0; i < length; i++) { @@ -2004,6 +2014,18 @@ static bool newline_and_indent(String *buffer, size_t level) buffer->fill(buffer->length() + level * 2, ' '); } +/** + Append a comma to separate elements in JSON arrays and objects. + @param buffer the string buffer + @param pretty true if pretty printing is enabled + @return true on error, false on success +*/ +static bool append_comma(String *buffer, bool pretty) { + // Append a comma followed by a blank space. If pretty printing is + // enabled, a newline will be added in front of the next element, so + // the blank space can be omitted. + return buffer->append(',') || (!pretty && buffer->append(' ')); +} /** Helper function which does all the heavy lifting for @@ -2037,7 +2059,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, case Json_dom::J_TIMESTAMP: { // Make sure the buffer has space for the datetime and the quotes. - if (buffer->reserve(MAX_DATE_STRING_REP_LENGTH + 2)) + if (reserve(buffer, MAX_DATE_STRING_REP_LENGTH + 2)) return true; /* purecov: inspected */ MYSQL_TIME t; wr.get_datetime(&t); @@ -2058,7 +2080,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, size_t array_len= wr.length(); for (uint32 i= 0; i < array_len; ++i) { - if (i > 0 && buffer->append(pretty ? "," : ", ")) + if (i > 0 && append_comma(buffer, pretty)) return true; /* purecov: inspected */ if (pretty && newline_and_indent(buffer, depth)) @@ -2077,14 +2099,14 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, break; } case Json_dom::J_BOOLEAN: - if (buffer->append(wr.get_boolean() ? "true" : "false")) + if (wr.get_boolean() ? buffer->append(STRING_WITH_LEN("true")) + : buffer->append(STRING_WITH_LEN("false"))) return true; /* purecov: inspected */ break; case Json_dom::J_DECIMAL: { int length= DECIMAL_MAX_STR_LENGTH + 1; - if (buffer->reserve(length)) - return true; /* purecov: inspected */ + if (reserve(buffer, length)) return true; char *ptr= const_cast(buffer->ptr()) + buffer->length(); my_decimal m; if (wr.get_decimal_data(&m) || @@ -2095,7 +2117,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, } case Json_dom::J_DOUBLE: { - if (buffer->reserve(MY_GCVT_MAX_FIELD_WIDTH + 1)) + if (reserve(buffer, MY_GCVT_MAX_FIELD_WIDTH + 1)) return true; /* purecov: inspected */ double d= wr.get_double(); size_t len= my_gcvt(d, MY_GCVT_ARG_DOUBLE, MY_GCVT_MAX_FIELD_WIDTH, @@ -2111,7 +2133,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, break; } case Json_dom::J_NULL: - if (buffer->append("null")) + if (buffer->append(STRING_WITH_LEN("null"))) return true; /* purecov: inspected */ break; case Json_dom::J_OBJECT: @@ -2123,7 +2145,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, for (Json_wrapper_object_iterator iter= wr.object_iterator(); !iter.empty(); iter.next()) { - if (!first && buffer->append(pretty ? "," : ", ")) + if (!first && append_comma(buffer, pretty)) return true; /* purecov: inspected */ first= false; @@ -2135,7 +2157,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, const char *key_data= key.c_str(); size_t key_length= key.length(); if (print_string(buffer, true, key_data, key_length) || - buffer->append(": ") || + buffer->append(':') || buffer->append(' ') || wrapper_to_string(iter.elt().second, buffer, true, pretty, func_name, depth)) return true; /* purecov: inspected */ @@ -2154,7 +2176,6 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, if (wr.get_data_length() > base64_encode_max_arg_length()) { /* purecov: begin inspected */ - buffer->append("\"\""); my_error(ER_INTERNAL_ERROR, MYF(0), "JSON: could not decode opaque data"); return true; @@ -2172,7 +2193,7 @@ static bool wrapper_to_string(const Json_wrapper &wr, String *buffer, // "base64:typeXX:" size_t pos= buffer->length(); - if (buffer->reserve(needed) || + if (reserve(buffer, needed) || base64_encode(wr.get_data(), wr.get_data_length(), const_cast(buffer->ptr() + pos))) return true; /* purecov: inspected */