diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 75e545ad711..7bb4e3bfebb 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -308,6 +308,26 @@ int json_path_compare(const json_path_t *a, const json_path_t *b, } +/* + Ensure *js can take 'add' more bytes, growing the buffer geometrically. + + json_nice() produces output that is larger than its input (indentation and + newlines), while String::append() enlarges the buffer by only a constant + increment once extra_alloc saturates (see realloc_with_extra()). Without + geometric growth the repeated reallocations copy the whole accumulated + result over and over, making the reformatting O(output_length^2). Reserving + in doubling steps keeps the whole operation linear. +*/ +static inline int json_nice_reserve(String *js, size_t add) +{ + size_t need= (size_t) js->length() + add; + if (need <= js->alloced_length()) + return 0; + size_t dbl= (size_t) js->alloced_length() * 2; + return js->realloc(need > dbl ? need : dbl); +} + + static int json_nice(json_engine_t *je, String *nice_js, Item_func_json_format::formats mode, int tab_size=4) { @@ -348,6 +368,16 @@ static int json_nice(json_engine_t *je, String *nice_js, do { curr_state= je->state; + /* + Reserve (geometrically) for every bounded structural append this + iteration may emit in any state: comma, indentation (append_tab), + colon, quotes and braces. This covers the JST_*_END cases too, where + closing a deep nest appends indentation with no other reserve. The + unbounded key/value text is reserved for separately below. + */ + if (json_nice_reserve(nice_js, + comma_len + colon_len + (size_t) (depth + 1) * tab_size + 16)) + goto error; switch (je->state) { case JST_KEY: @@ -370,6 +400,8 @@ static int json_nice(json_engine_t *je, String *nice_js, append_tab(nice_js, depth, tab_size)) goto error; + if (json_nice_reserve(nice_js, (size_t) (key_end - key_start) + 8)) + goto error; nice_js->append('"'); if (append_simple(nice_js, key_start, key_end - key_start)) goto error; @@ -393,6 +425,9 @@ static int json_nice(json_engine_t *je, String *nice_js, goto error; if (json_value_scalar(je)) { + if (json_nice_reserve(nice_js, + (size_t) (je->value_end - je->value_begin) + 8)) + goto error; if (append_simple(nice_js, je->value_begin, je->value_end - je->value_begin)) goto error;