revision-id: 95e8568ac3d360ab14cbfbfa2eaa32078ae4fd7b (mariadb-10.3.10-81-g95e8568) parent(s): 13cd4cf436c1f7c38c6d9dfd8077c98fc655336b author: Igor Babaev committer: Igor Babaev timestamp: 2018-11-14 21:36:15 -0800 message: MDEV-16506 CTE with overflow of integer type returns wrong results When adding rows to the table defined by a recursive CTE the method select_unit::send_data() must check weather new generated rows contain out of range data. If so it should report an error. Otherwise the truncation of data can cause endless generation of identical groups of rows. --- mysql-test/main/cte_recursive.result | 22 ++++++++++++++++++++++ mysql-test/main/cte_recursive.test | 21 +++++++++++++++++++++ sql/sql_class.cc | 9 ++++++++- sql/sql_class.h | 4 ++++ sql/sql_union.cc | 12 ++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result index 9e934b1..2b95566 100644 --- a/mysql-test/main/cte_recursive.result +++ b/mysql-test/main/cte_recursive.result @@ -3895,4 +3895,26 @@ a 0 NULL DROP TABLE t1; +# +# MDEV-16506: recursion produces out of range values +# +WITH RECURSIVE qn AS +( +SELECT 1 AS a FROM dual +UNION ALL +SELECT a*2000 FROM qn WHERE a<1000000 +) +SELECT * FROM qn; +a +1 +2000 +4000000 +WITH RECURSIVE qn AS +( +SELECT 1 AS a FROM dual +UNION ALL +SELECT a*2000 FROM qn WHERE a<100000000000 +) +SELECT * FROM qn; +ERROR 22003: Out of range value for column 'a' at row 1 # End of 10.3 tests diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test index 7980904..3eb1643 100644 --- a/mysql-test/main/cte_recursive.test +++ b/mysql-test/main/cte_recursive.test @@ -2726,4 +2726,25 @@ SELECT * FROM cte; DROP TABLE t1; +--echo # +--echo # MDEV-16506: recursion produces out of range values +--echo # + +WITH RECURSIVE qn AS +( + SELECT 1 AS a FROM dual + UNION ALL + SELECT a*2000 FROM qn WHERE a<1000000 +) +SELECT * FROM qn; + +--error ER_WARN_DATA_OUT_OF_RANGE +WITH RECURSIVE qn AS +( + SELECT 1 AS a FROM dual + UNION ALL + SELECT a*2000 FROM qn WHERE a<100000000000 +) +SELECT * FROM qn; + --echo # End of 10.3 tests diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e4f3171..030fc7e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -627,6 +627,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock) bootstrap(0), derived_tables_processing(FALSE), waiting_on_group_commit(FALSE), has_waiter(FALSE), + aborting_warning(0), spcont(NULL), m_parser_state(NULL), #if defined(ENABLED_DEBUG_SYNC) @@ -1028,6 +1029,12 @@ Sql_condition* THD::raise_condition(uint sql_errno, level= Sql_condition::WARN_LEVEL_ERROR; } + if ((level == Sql_condition::WARN_LEVEL_WARN) && + aborting_warning == sql_errno) + { + level= Sql_condition::WARN_LEVEL_ERROR; + } + if (handle_condition(sql_errno, sqlstate, &level, msg, &cond)) DBUG_RETURN(cond); @@ -1573,7 +1580,7 @@ void THD::reset_for_reuse() client_capabilities= 0; peer_port= 0; query_name_consts= 0; // Safety - abort_on_warning= 0; + aborting_warning= 0; free_connection_done= 0; m_command= COM_CONNECT; #if defined(ENABLED_PROFILING) diff --git a/sql/sql_class.h b/sql/sql_class.h index 1a7eb94..6f91d4f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3140,6 +3140,7 @@ class THD :public Statement, the query. 0 if no error on the master. */ int slave_expected_error; + uint aborting_warning; enum_sql_command last_sql_command; // Last sql_command exceuted in mysql_execute_command() sp_rcontext *spcont; // SP runtime context @@ -5561,6 +5562,7 @@ class select_unit :public select_result_interceptor */ virtual bool postponed_prepare(List<Item> &types) { return false; } + virtual bool is_recursive() { return false; } int send_data(List<Item> &items); bool send_eof(); virtual bool flush(); @@ -5603,6 +5605,8 @@ class select_union_recursive :public select_unit select_unit(thd_arg), incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {}; + bool is_recursive() { return true; } + int send_data(List<Item> &items); bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 6368ed8..803ec57 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -109,6 +109,7 @@ void select_unit::change_select() int select_unit::send_data(List<Item> &values) { int rc; + uint aborting_warning_save=0; int not_reported_error= 0; if (unit->offset_limit_cnt) { // using limit offset,count @@ -119,6 +120,12 @@ int select_unit::send_data(List<Item> &values) return 0; if (table->no_rows_with_nulls) table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; + if (is_recursive()) + { + aborting_warning_save= thd->aborting_warning; + thd->aborting_warning= ER_WARN_DATA_OUT_OF_RANGE; + thd->count_cuted_fields= CHECK_FIELD_WARN; + } if (intersect_mark) { fill_record(thd, table, table->field + 1, values, TRUE, FALSE); @@ -126,6 +133,11 @@ int select_unit::send_data(List<Item> &values) } else fill_record(thd, table, table->field, values, TRUE, FALSE); + if (is_recursive()) + { + thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; + thd->aborting_warning= aborting_warning_save; + } if (unlikely(thd->is_error())) { rc= 1;