diff --git a/include/json_lib.h b/include/json_lib.h index bb649928eaa..e570e2a9d17 100644 --- a/include/json_lib.h +++ b/include/json_lib.h @@ -222,6 +222,7 @@ typedef struct st_json_engine_t int stack[JSON_DEPTH_LIMIT]; /* Keeps the stack of nested JSON structures. */ int stack_p; /* The 'stack' pointer. */ + volatile uchar *killed_ptr; } json_engine_t; diff --git a/mysql-test/main/func_json_notembedded.result b/mysql-test/main/func_json_notembedded.result new file mode 100644 index 00000000000..be879dfc9d6 --- /dev/null +++ b/mysql-test/main/func_json_notembedded.result @@ -0,0 +1,42 @@ +set global max_allowed_packet=1073741824; +connect u,localhost,root; +# +# MDEV-24909 JSON functions don't respect KILL QUERY / max_statement_time limit +# +set group_concat_max_len= 4294967295; +set @obj=concat_ws('','{', repeat('"a":"b",', 125000000/2), '"c":"d"}'); +set @arr=concat_ws('','[', repeat('1234567,', 125000000/2), '2345678]'); +select length(@obj), length(@arr); +length(@obj) length(@arr) +500000009 500000009 +set max_statement_time=0.0001; +select json_array_append(@arr, '$[0]', 1); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_array_insert(@arr, '$[0]', 1); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_insert(@obj, '$.meta', 1); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_compact(@arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_detailed(@arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_loose(@arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_merge(@obj, @arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_merge_patch(@obj, @obj); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_merge_preserve(@obj, @arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_remove(@obj,'$.foo'); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_replace(@obj,'$.foo',1); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_set(@arr,'$[1000]',1); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +disconnect u; +connection default; +set global max_allowed_packet=default; +# +# End of 10.6 tests +# diff --git a/mysql-test/main/func_json_notembedded.test b/mysql-test/main/func_json_notembedded.test new file mode 100644 index 00000000000..328d9974c77 --- /dev/null +++ b/mysql-test/main/func_json_notembedded.test @@ -0,0 +1,37 @@ +source include/have_profiling.inc; +source include/not_embedded.inc; + +set global max_allowed_packet=1073741824; +connect u,localhost,root; + +--echo # +--echo # MDEV-24909 JSON functions don't respect KILL QUERY / max_statement_time limit +--echo # +set group_concat_max_len= 4294967295; + +set @obj=concat_ws('','{', repeat('"a":"b",', 125000000/2), '"c":"d"}'); +set @arr=concat_ws('','[', repeat('1234567,', 125000000/2), '2345678]'); +select length(@obj), length(@arr); + +set max_statement_time=0.0001; +disable_abort_on_error; +select json_array_append(@arr, '$[0]', 1); +select json_array_insert(@arr, '$[0]', 1); +select json_insert(@obj, '$.meta', 1); +select json_compact(@arr); +select json_detailed(@arr); +select json_loose(@arr); +select json_merge(@obj, @arr); +select json_merge_patch(@obj, @obj); +select json_merge_preserve(@obj, @arr); +select json_remove(@obj,'$.foo'); +select json_replace(@obj,'$.foo',1); +select json_set(@arr,'$[1000]',1); +enable_abort_on_error; +disconnect u; +connection default; +set global max_allowed_packet=default; + +--echo # +--echo # End of 10.6 tests +--echo # diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index abd41b8cf9f..829b9952e15 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -240,7 +240,7 @@ handle_value: }; } while (json_scan_next(je) == 0); - return je->s.error; + return je->s.error || *je->killed_ptr; error: return 1; @@ -1653,6 +1653,7 @@ String *Item_func_json_array_append::val_str(String *str) uint n_arg, n_path; size_t str_rest_len; const uchar *ar_end; + THD *thd= current_thd; DBUG_ASSERT(fixed()); @@ -1680,6 +1681,7 @@ String *Item_func_json_array_append::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; c_path->cur_step= c_path->p.steps; @@ -1760,6 +1762,7 @@ String *Item_func_json_array_append::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -1769,6 +1772,7 @@ js_error: report_json_error(js, &je, 0); return_null: + thd->check_killed(); // to get the error message right null_value= 1; return 0; } @@ -1779,6 +1783,7 @@ String *Item_func_json_array_insert::val_str(String *str) json_engine_t je; String *js= args[0]->val_json(&tmp_js); uint n_arg, n_path; + THD *thd= current_thd; DBUG_ASSERT(fixed()); @@ -1816,6 +1821,7 @@ String *Item_func_json_array_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; c_path->cur_step= c_path->p.steps; @@ -1855,7 +1861,7 @@ String *Item_func_json_array_insert::val_str(String *str) goto js_error; } - if (unlikely(je.s.error)) + if (unlikely(je.s.error || *je.killed_ptr)) goto js_error; str->length(0); @@ -1899,6 +1905,7 @@ String *Item_func_json_array_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -1907,6 +1914,7 @@ String *Item_func_json_array_insert::val_str(String *str) js_error: report_json_error(js, &je, 0); return_null: + thd->check_killed(); // to get the error message right null_value= 1; return 0; } @@ -2165,6 +2173,7 @@ String *Item_func_json_merge::val_str(String *str) json_engine_t je1, je2; String *js1= args[0]->val_json(&tmp_js1), *js2=NULL; uint n_arg; + THD *thd= current_thd; LINT_INIT(js2); if (args[0]->null_value) @@ -2181,9 +2190,11 @@ String *Item_func_json_merge::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uchar*)&thd->killed; json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), (const uchar *) js2->ptr() + js2->length()); + je2.killed_ptr= (uchar*)&thd->killed; if (do_merge(str, &je1, &je2)) goto error_return; @@ -2205,6 +2216,7 @@ String *Item_func_json_merge::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je1, str, Item_func_json_format::LOOSE)) goto error_return; @@ -2216,6 +2228,7 @@ error_return: report_json_error(js1, &je1, 0); if (je2.s.error) report_json_error(js2, &je2, n_arg); + thd->check_killed(); // to get the error message right null_return: null_value= 1; return NULL; @@ -2465,6 +2478,7 @@ String *Item_func_json_merge_patch::val_str(String *str) String *js1= args[0]->val_json(&tmp_js1), *js2=NULL; uint n_arg; bool empty_result, merge_to_null; + THD *thd= current_thd; /* To report errors properly if some JSON is invalid. */ je1.s.error= je2.s.error= 0; @@ -2481,6 +2495,7 @@ String *Item_func_json_merge_patch::val_str(String *str) json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), (const uchar *) js2->ptr() + js2->length()); + je2.killed_ptr= (uchar*)&thd->killed; if (merge_to_null) { @@ -2502,6 +2517,7 @@ String *Item_func_json_merge_patch::val_str(String *str) json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uchar*)&thd->killed; if (do_merge_patch(str, &je1, &je2, &empty_result)) goto error_return; @@ -2530,6 +2546,7 @@ cont_point: json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je1, str, Item_func_json_format::LOOSE)) goto error_return; @@ -2541,6 +2558,7 @@ error_return: report_json_error(js1, &je1, 0); if (je2.s.error) report_json_error(js2, &je2, n_arg); + thd->check_killed(); // to get the error message right null_return: null_value= 1; return NULL; @@ -2777,6 +2795,7 @@ String *Item_func_json_insert::val_str(String *str) String *js= args[0]->val_json(&tmp_js); uint n_arg, n_path; json_string_t key_name; + THD *thd= current_thd; DBUG_ASSERT(fixed()); @@ -2817,6 +2836,7 @@ String *Item_func_json_insert::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (c_path->p.last_step < c_path->p.steps) goto v_found; @@ -2998,6 +3018,7 @@ continue_point: json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -3005,6 +3026,7 @@ continue_point: js_error: report_json_error(js, &je, 0); + thd->check_killed(); // to get the error message right return_null: null_value= 1; return 0; @@ -3028,6 +3050,7 @@ String *Item_func_json_remove::val_str(String *str) String *js= args[0]->val_json(&tmp_js); uint n_arg, n_path; json_string_t key_name; + THD *thd= current_thd; DBUG_ASSERT(fixed()); @@ -3074,6 +3097,7 @@ String *Item_func_json_remove::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; c_path->cur_step= c_path->p.steps; @@ -3182,6 +3206,7 @@ v_found: json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -3189,6 +3214,7 @@ v_found: return str; js_error: + thd->check_killed(); // to get the error message right report_json_error(js, &je, 0); null_return: null_value= 1; @@ -3579,6 +3605,7 @@ String *Item_func_json_format::val_str(String *str) String *js= args[0]->val_json(&tmp_js); json_engine_t je; int tab_size= 4; + THD *thd= current_thd; if ((null_value= args[0]->null_value)) return 0; @@ -3602,11 +3629,13 @@ String *Item_func_json_format::val_str(String *str) json_scan_start(&je, js->charset(), (const uchar *) js->ptr(), (const uchar *) js->ptr()+js->length()); + je.killed_ptr= (uchar*)&thd->killed; if (json_nice(&je, str, fmt, tab_size)) { null_value= 1; report_json_error(js, &je, 0); + thd->check_killed(); // to get the error message right return 0; } diff --git a/strings/json_lib.c b/strings/json_lib.c index 49f29903ed5..e55394bbeb8 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -807,10 +807,13 @@ static json_state_handler json_actions[NR_JSON_STATES][NR_C_CLASSES]= int json_scan_start(json_engine_t *je, CHARSET_INFO *i_cs, const uchar *str, const uchar *end) { + static const uchar no_time_to_die= 0; + json_string_setup(&je->s, i_cs, str, end); je->stack[0]= JST_DONE; je->stack_p= 0; je->state= JST_VALUE; + je->killed_ptr = (uchar*)&no_time_to_die; return 0; } @@ -971,7 +974,7 @@ int json_scan_next(json_engine_t *j) int t_next; get_first_nonspace(&j->s, &t_next, &j->sav_c_len); - return json_actions[j->state][t_next](j); + return *j->killed_ptr || json_actions[j->state][t_next](j); }