From 5e04903f592bb425d52f9fa18388e1e64b67497f Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Sat, 22 Jan 2022 16:51:21 +0530 Subject: MDEV-26843: Inconsistent behavior of ROW_NUMBER upon resignalling from function Analysis: m_current_row_for_warning is reset to 0 earlier so new value '0' of the counter gets recorded for error condition. Fix: reset m_current_row_for_warning after other error conditions. --- mysql-test/main/get_diagnostics.result | 79 ++++++++++++++++++++ mysql-test/main/get_diagnostics.test | 87 ++++++++++++++++++++++ mysql-test/main/type_decimal.result | 2 +- .../suite/sysschema/r/pr_table_exists.result | 2 +- sql/sql_base.cc | 8 +- sql/sql_base.h | 4 +- sql/sql_class.h | 3 + sql/sql_do.cc | 2 +- sql/sql_error.cc | 1 - sql/sql_insert.cc | 15 ++-- sql/sql_load.cc | 10 +-- sql/sql_prepare.cc | 8 +- sql/sql_select.cc | 2 +- sql/sql_update.cc | 5 +- 14 files changed, 201 insertions(+), 27 deletions(-) diff --git a/mysql-test/main/get_diagnostics.result b/mysql-test/main/get_diagnostics.result index 2e749fa21d7..dd06c1baf20 100644 --- a/mysql-test/main/get_diagnostics.result +++ b/mysql-test/main/get_diagnostics.result @@ -1811,3 +1811,82 @@ SELECT @n; @n 4 DROP TABLE t; +# +# MDEV-26843: Inconsistent behavior of ROW_NUMBER upon resignalling from function +# +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN +DECLARE i INT DEFAULT -1; +DECLARE EXIT HANDLER FOR 1318 +BEGIN +GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; +RESIGNAL; +RETURN -2; +END; +SELECT f() INTO i; +RETURN i; +END | +SELECT f2(); +ERROR 42000: Incorrect number of arguments for FUNCTION test.f; expected 1, got 0 +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +@row_number_in_f2 @msg_in_f2 +1 Incorrect number of arguments for FUNCTION test.f; expected 1, got 0 +SELECT @row_number_outside, @msg_outside; +@row_number_outside @msg_outside +1 Incorrect number of arguments for FUNCTION test.f; expected 1, got 0 +DROP FUNCTION f2; +DROP FUNCTION f; +CREATE TABLE t1 (a INT); +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN +DECLARE i INT DEFAULT -1; +DECLARE EXIT HANDLER FOR 1305 +BEGIN +GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; +RESIGNAL; +RETURN -2; +END; +INSERT t1 VALUES (f(1)), (f(2)), (f1()); +RETURN i; +END | +SELECT f2(); +ERROR 42000: FUNCTION test.f1 does not exist +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +@row_number_in_f2 @msg_in_f2 +3 FUNCTION test.f1 does not exist +SELECT @row_number_outside, @msg_outside; +@row_number_outside @msg_outside +3 FUNCTION test.f1 does not exist +DROP FUNCTION f2; +DROP FUNCTION f; +DROP TABLE t1; +CREATE TABLE t1 (a INT); +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN +DECLARE i INT DEFAULT -1; +DECLARE EXIT HANDLER FOR 1305 +BEGIN +GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; +RESIGNAL; +RETURN -2; +END; +INSERT t1 VALUES (f1(1)), (f(2)), (f(3)); +RETURN i; +END | +SELECT f2(); +ERROR 42000: FUNCTION test.f1 does not exist +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +@row_number_in_f2 @msg_in_f2 +1 FUNCTION test.f1 does not exist +SELECT @row_number_outside, @msg_outside; +@row_number_outside @msg_outside +1 FUNCTION test.f1 does not exist +DROP FUNCTION f2; +DROP FUNCTION f; +DROP TABLE t1; diff --git a/mysql-test/main/get_diagnostics.test b/mysql-test/main/get_diagnostics.test index e8d81dca1e6..386a8a742d8 100644 --- a/mysql-test/main/get_diagnostics.test +++ b/mysql-test/main/get_diagnostics.test @@ -1687,3 +1687,90 @@ GET DIAGNOSTICS CONDITION 3 @n = ROW_NUMBER; SELECT @n; DROP TABLE t; + +--echo # +--echo # MDEV-26843: Inconsistent behavior of ROW_NUMBER upon resignalling from function +--echo # +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +DELIMITER |; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN + DECLARE i INT DEFAULT -1; + DECLARE EXIT HANDLER FOR 1318 + BEGIN + GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; + RESIGNAL; + RETURN -2; + END; + SELECT f() INTO i; + RETURN i; +END | +DELIMITER ;| + +--error ER_SP_WRONG_NO_OF_ARGS +SELECT f2(); + +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +SELECT @row_number_outside, @msg_outside; + +DROP FUNCTION f2; +DROP FUNCTION f; + +CREATE TABLE t1 (a INT); + +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +DELIMITER |; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN + DECLARE i INT DEFAULT -1; + DECLARE EXIT HANDLER FOR 1305 + BEGIN + GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; + RESIGNAL; + RETURN -2; + END; + INSERT t1 VALUES (f(1)), (f(2)), (f1()); + RETURN i; +END | +DELIMITER ;| + +--error ER_SP_DOES_NOT_EXIST + SELECT f2(); + +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +SELECT @row_number_outside, @msg_outside; + +DROP FUNCTION f2; +DROP FUNCTION f; +DROP TABLE t1; + +CREATE TABLE t1 (a INT); + +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT RETURN a; +DELIMITER |; +CREATE OR REPLACE FUNCTION f2() RETURNS INT +BEGIN + DECLARE i INT DEFAULT -1; + DECLARE EXIT HANDLER FOR 1305 + BEGIN + GET DIAGNOSTICS CONDITION 1 @row_number_in_f2= ROW_NUMBER, @msg_in_f2= MESSAGE_TEXT; + RESIGNAL; + RETURN -2; + END; + INSERT t1 VALUES (f1(1)), (f(2)), (f(3)); + RETURN i; +END | +DELIMITER ;| + +--error ER_SP_DOES_NOT_EXIST + SELECT f2(); + +GET DIAGNOSTICS CONDITION 1 @row_number_outside= ROW_NUMBER, @msg_outside= MESSAGE_TEXT; +SELECT @row_number_in_f2, @msg_in_f2; +SELECT @row_number_outside, @msg_outside; + +DROP FUNCTION f2; +DROP FUNCTION f; +DROP TABLE t1; diff --git a/mysql-test/main/type_decimal.result b/mysql-test/main/type_decimal.result index 7bf3b52970a..70367ef246c 100644 --- a/mysql-test/main/type_decimal.result +++ b/mysql-test/main/type_decimal.result @@ -1091,7 +1091,7 @@ a Warnings: Warning 1916 Got overflow when converting '' to DECIMAL. Value truncated Warning 1292 Truncated incorrect DECIMAL value: '.00000000000000000000000000000000000001e111111111111111111111' -Warning 1264 Out of range value for column 'a' at row 0 +Warning 1264 Out of range value for column 'a' at row 1 CREATE TABLE t1 (str VARCHAR(128), comment VARCHAR(128)); INSERT INTO t1 VALUES ('0e111111111111111111111', 'Zero mantissa and a huge positive exponent'), diff --git a/mysql-test/suite/sysschema/r/pr_table_exists.result b/mysql-test/suite/sysschema/r/pr_table_exists.result index 459ad1639e7..c9a0d0c31b3 100644 --- a/mysql-test/suite/sysschema/r/pr_table_exists.result +++ b/mysql-test/suite/sysschema/r/pr_table_exists.result @@ -21,7 +21,7 @@ SELECT @exists; SET @identifier := REPEAT('a', 65); CALL sys.table_exists(@identifier, 't1', @exists); -ERROR 22001: Data too long for column 'in_db' at row 1 +ERROR 22001: Data too long for column 'in_db' at row 0 CALL sys.table_exists('test', @identifier, @exists); ERROR 22001: Data too long for column 'in_table' at row 0 DROP TEMPORARY TABLE t1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 414bb5efa25..5bde1ea9232 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7645,7 +7645,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, List &fields, enum_column_usage column_usage, List *sum_func_list, List *pre_fix, - bool allow_sum_func) + bool allow_sum_func, bool reset_counter) { Item *item; enum_column_usage saved_column_usage= thd->column_usage; @@ -7669,6 +7669,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_ASSERT(thd->lex->current_select->master_unit()->first_select() ->nest_level == thd->lex->current_select->nest_level); + if (reset_counter) + thd->get_stmt_da()->reset_current_row_for_warning(1); if (allow_sum_func) thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level); thd->where= THD::DEFAULT_WHERE; @@ -7749,6 +7751,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, thd->lex->allow_sum_func= save_allow_sum_func; thd->column_usage= saved_column_usage; + if (reset_counter) + thd->get_stmt_da()->reset_current_row_for_warning(0); DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); DBUG_RETURN(MY_TEST(thd->is_error())); } @@ -7770,7 +7774,7 @@ int setup_returning_fields(THD* thd, TABLE_LIST* table_list) return setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL, thd->lex->returning(), true) || setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list, - MARK_COLUMNS_READ, NULL, NULL, false); + MARK_COLUMNS_READ, NULL, NULL, false, 0); } diff --git a/sql/sql_base.h b/sql/sql_base.h index 5b449fdddac..9c27307b0ba 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -185,7 +185,7 @@ int setup_returning_fields(THD* thd, TABLE_LIST* table_list); bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, List &item, enum_column_usage column_usage, List *sum_func_list, List *pre_fix, - bool allow_sum_func); + bool allow_sum_func, bool reset_counter); void unfix_fields(List &items); bool fill_record(THD * thd, TABLE *table_arg, List &fields, List &values, bool ignore_errors, bool update); @@ -380,7 +380,7 @@ inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_ASSERT(thd->lex->current_select == first); first->no_wrap_view_item= TRUE; res= setup_fields(thd, ref_pointer_array, item, column_usage, - sum_func_list, NULL, allow_sum_func); + sum_func_list, NULL, allow_sum_func, 0); first->no_wrap_view_item= FALSE; return res; } diff --git a/sql/sql_class.h b/sql/sql_class.h index df9d89b5aff..0dfa7ac6602 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4277,7 +4277,10 @@ public: { DBUG_ENTER("clear_error"); if (get_stmt_da()->is_error() || clear_diagnostics) + { get_stmt_da()->reset_diagnostics_area(); + get_stmt_da()->reset_current_row_for_warning(0); + } is_slave_error= 0; if (killed == KILL_BAD_DATA) reset_killed(); diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 5a7bca2774c..e556d9c0993 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -29,7 +29,7 @@ bool mysql_do(THD *thd, List &values) List_iterator li(values); Item *value; DBUG_ENTER("mysql_do"); - if (setup_fields(thd, Ref_ptr_array(), values, COLUMNS_READ, 0, NULL, 0)) + if (setup_fields(thd, Ref_ptr_array(), values, COLUMNS_READ, 0, NULL, 0, 0)) DBUG_RETURN(TRUE); while ((value = li++)) (void) value->is_null(); diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 85be61c34ef..ffc4a34cee4 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -558,7 +558,6 @@ void Warning_info::clear(ulonglong new_id) free_memory(); memset(m_warn_count, 0, sizeof(m_warn_count)); m_current_statement_warn_count= 0; - m_current_row_for_warning= 0; clear_error_condition(); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 707b8a0d3bf..17d69cd0e71 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -276,7 +276,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, unfix_fields(fields); res= setup_fields(thd, Ref_ptr_array(), - fields, MARK_COLUMNS_WRITE, 0, NULL, 0); + fields, MARK_COLUMNS_WRITE, 0, NULL, 0, 0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -388,7 +388,7 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, /* Check the fields we are going to modify */ if (setup_fields(thd, Ref_ptr_array(), - update_fields, MARK_COLUMNS_WRITE, 0, NULL, 0)) + update_fields, MARK_COLUMNS_WRITE, 0, NULL, 0, 0)) return -1; if (insert_table_list->is_view() && @@ -839,7 +839,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, goto abort; } if (setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_READ, 0, NULL, 0)) + *values, MARK_COLUMNS_READ, 0, NULL, 0, 0)) goto abort; switch_to_nullable_trigger_fields(*values, table); } @@ -1671,13 +1671,14 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, res= setup_returning_fields(thd, table_list) || setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_READ, 0, NULL, 0) || + *values, MARK_COLUMNS_READ, 0, NULL, 0, 1) || check_insert_fields(thd, context->table_list, fields, *values, !insert_into_view, 0, &map); + if (!res) res= setup_fields(thd, Ref_ptr_array(), - update_values, MARK_COLUMNS_READ, 0, NULL, 0); + update_values, MARK_COLUMNS_READ, 0, NULL, 0, 0); if (!res && duplic == DUP_UPDATE) { @@ -3866,7 +3867,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) res= (setup_returning_fields(thd, table_list) || setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, 0, - 0) || + 0, 0) || check_insert_fields(thd, table_list, *fields, values, !insert_into_view, 1, &map)); @@ -3923,7 +3924,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) } res= res || setup_fields(thd, Ref_ptr_array(), *info.update_values, - MARK_COLUMNS_READ, 0, NULL, 0); + MARK_COLUMNS_READ, 0, NULL, 0, 0); if (!res) { /* diff --git a/sql/sql_load.cc b/sql/sql_load.cc index fe574db528f..0af5530b47d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -472,9 +472,9 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list, in this case. */ if (setup_fields(thd, Ref_ptr_array(), - set_fields, MARK_COLUMNS_WRITE, 0, NULL, 0) || + set_fields, MARK_COLUMNS_WRITE, 0, NULL, 0, 0) || setup_fields(thd, Ref_ptr_array(), - set_values, MARK_COLUMNS_READ, 0, NULL, 0)) + set_values, MARK_COLUMNS_READ, 0, NULL, 0, 0)) DBUG_RETURN(TRUE); } else @@ -482,14 +482,14 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list, scope_cleaner.release(); /* TODO: use this conds for 'WITH CHECK OPTIONS' */ if (setup_fields(thd, Ref_ptr_array(), - fields_vars, MARK_COLUMNS_WRITE, 0, NULL, 0) || + fields_vars, MARK_COLUMNS_WRITE, 0, NULL, 0, 0) || setup_fields(thd, Ref_ptr_array(), - set_fields, MARK_COLUMNS_WRITE, 0, NULL, 0) || + set_fields, MARK_COLUMNS_WRITE, 0, NULL, 0, 0) || check_that_all_fields_are_given_values(thd, table, table_list)) DBUG_RETURN(TRUE); /* Fix the expressions in SET clause */ if (setup_fields(thd, Ref_ptr_array(), - set_values, MARK_COLUMNS_READ, 0, NULL, 0)) + set_values, MARK_COLUMNS_READ, 0, NULL, 0, 0)) DBUG_RETURN(TRUE); } switch_to_nullable_trigger_fields(fields_vars, table); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 68dd670787c..739f744f058 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1347,7 +1347,7 @@ static bool mysql_test_insert_common(Prepared_statement *stmt, goto error; } if (setup_fields(thd, Ref_ptr_array(), - *values, COLUMNS_READ, 0, NULL, 0)) + *values, COLUMNS_READ, 0, NULL, 0, 0)) goto error; thd->get_stmt_da()->inc_current_row_for_warning(); } @@ -1481,7 +1481,7 @@ static int mysql_test_update(Prepared_statement *stmt, #endif thd->lex->first_select_lex()->no_wrap_view_item= TRUE; res= setup_fields(thd, Ref_ptr_array(), - select->item_list, MARK_COLUMNS_READ, 0, NULL, 0); + select->item_list, MARK_COLUMNS_READ, 0, NULL, 0, 0); thd->lex->first_select_lex()->no_wrap_view_item= FALSE; if (res) goto error; @@ -1493,7 +1493,7 @@ static int mysql_test_update(Prepared_statement *stmt, table_list->register_want_access(SELECT_ACL); #endif if (setup_fields(thd, Ref_ptr_array(), - stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0) || + stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0, 0) || check_unique_table(thd, table_list)) goto error; /* TODO: here we should send types of placeholders to the client. */ @@ -1669,7 +1669,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, DT_INIT | DT_PREPARE)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, Ref_ptr_array(), - *values, COLUMNS_READ, 0, NULL, 0)); + *values, COLUMNS_READ, 0, NULL, 0, 0)); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ae5cd82da4c..5dd5e87d33e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1395,7 +1395,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, } if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ, - &all_fields, &select_lex->pre_fix, 1)) + &all_fields, &select_lex->pre_fix, 1, 0)) DBUG_RETURN(-1); thd->lex->current_select->context_analysis_place= save_place; rand_table_in_field_list= select_lex->select_list_tables & RAND_TABLE_BIT; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 539bea958e8..6ed9503c967 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -516,7 +516,8 @@ int mysql_update(THD *thd, table_list->grant.want_privilege= table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); #endif - if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, NULL, 0)) + if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, + NULL, 0, 0)) { free_underlaid_joins(thd, select_lex); DBUG_RETURN(1); /* purecov: inspected */ @@ -2081,7 +2082,7 @@ int multi_update::prepare(List ¬_used_values, */ int error= setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_READ, 0, NULL, 0); + *values, MARK_COLUMNS_READ, 0, NULL, 0, 0); ti.rewind(); while ((table_ref= ti++)) -- cgit v1.2.1