summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2022-03-27 14:02:34 +0700
committerDmitry Shulga <dmitry.shulga@mariadb.com>2022-03-30 16:11:18 +0700
commitbdba1d46bbf08044274cbb5a94a6cb395485c0af (patch)
tree9b5953ab51f7d4e22f2793c24e03afc0d0ab0c46
parent33ff18627ea009709bb0ba55b68f873e6c6c784c (diff)
downloadmariadb-git-bdba1d46bbf08044274cbb5a94a6cb395485c0af.tar.gz
MDEV-19631: Assertion `0' failed in st_select_lex_unit::optimize or different plan upon 2nd execution of PS with EXPLAIN
Second execution of a prepared statement for a query containing a constant subquery with union that can be optimized away, could result in server abnormal termination for debug build or incorrect result set output for release build. For example, the following test case crashes a server built with debug on second run of the statement EXECUTE stmt CREATE TABLE t1 (a INT); PREPARE stmt FROM 'EXPLAIN SELECT * FROM t1 HAVING 6 IN ( SELECT 6 UNION SELECT 5 )'; EXECUTE stmt; EXECUTE stmt; The reason for incorrect result set output or abnormal server termination is careless working with the data member fake_select_lex->options inside the function mysql_explain_union(). Once the flag SELECT_DESCRIBE is set in the data member fake_select_lex->option before calling the methods SELECT_LEX_UNIT::prepare/SELECT_LEX_UNIT::execute the original value of the option is no longer restored. As a consequence, next time the prepared statement is re-executed we have the fake_select_lex with the flag SELECT_DESCRIBE set in the data member fake_select_lex->option, that is incorrect. In result, the method Item_subselect::assigned() is not invoked during evaluation of a constant condition (constant subquery with union) that being performed on OPTIMIZE phase of query handling. This leads to the fact that records in the temporary table are not deleted before calling table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL) in the method st_select_lex_unit::optimize(). In result table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL) returns error and DBUG_ASSERT(0) is fired. Stack trace to the line where the error generated on re-enabling indexes for next subselect iteration is below: st_select_lex_unit::optimize (at sql_union.cc:954) handler::ha_enable_indexes (at handler.cc:4338) ha_heap::enable_indexes (at ha_heap.cc:519) heap_enable_indexes (at hp_clear.c:164) The code snippet to clarify raising the error is also listed: int heap_enable_indexes(HP_INFO *info) { int error= 0; HP_SHARE *share= info->s; if (share->data_length || share->index_length) error= HA_ERR_CRASHED; <<== set error the value HA_ERR_CRASHED since share->data_length != 0 To fix this issue the original value of unit->fake_select_lex->options has to be saved before setting the flag SELECT_DESCRIBE and restored on return from invocation of SELECT_LEX_UNIT::prepare/SELECT_LEX_UNIT::execute
-rw-r--r--mysql-test/r/ps.result22
-rw-r--r--mysql-test/t/ps.test13
-rw-r--r--sql/sql_select.cc6
3 files changed, 41 insertions, 0 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 1cd41e5b3a4..81167bc85c0 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -5487,5 +5487,27 @@ a
DEALLOCATE PREPARE stmt;
DROP VIEW v1;
DROP TABLE t1;
+#
+# MDEV-19631: Assertion `0' failed in st_select_lex_unit::optimize or
+# different plan upon 2nd execution of PS with EXPLAIN
+#
+CREATE TABLE t1 (a INT);
+PREPARE stmt FROM 'EXPLAIN SELECT * FROM t1 HAVING 6 IN ( SELECT 6 UNION SELECT 5 )';
+EXECUTE stmt;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
+# Without the patch the second execution of the 'stmt' prepared statement
+# would result in server crash.
+EXECUTE stmt;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
+DEALLOCATE PREPARE stmt;
+DROP TABLE t1;
# End of 10.2 tests
#
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 4097d28a949..81d8e8e0fed 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -4976,6 +4976,19 @@ DEALLOCATE PREPARE stmt;
DROP VIEW v1;
DROP TABLE t1;
+--echo #
+--echo # MDEV-19631: Assertion `0' failed in st_select_lex_unit::optimize or
+--echo # different plan upon 2nd execution of PS with EXPLAIN
+--echo #
+CREATE TABLE t1 (a INT);
+PREPARE stmt FROM 'EXPLAIN SELECT * FROM t1 HAVING 6 IN ( SELECT 6 UNION SELECT 5 )';
+EXECUTE stmt;
+--echo # Without the patch the second execution of the 'stmt' prepared statement
+--echo # would result in server crash.
+EXECUTE stmt;
+# Cleanup
+DEALLOCATE PREPARE stmt;
+DROP TABLE t1;
--echo # End of 10.2 tests
--echo #
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a3e8b453897..22d3597de16 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -25400,14 +25400,20 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
if (unit->is_union() || unit->fake_select_lex)
{
+ ulonglong save_options= 0;
+
if (unit->union_needs_tmp_table() && unit->fake_select_lex)
{
+ save_options= unit->fake_select_lex->options;
unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization
unit->fake_select_lex->type= "UNION RESULT";
unit->fake_select_lex->options|= SELECT_DESCRIBE;
}
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec();
+
+ if (unit->union_needs_tmp_table() && unit->fake_select_lex)
+ unit->fake_select_lex->options= save_options;
}
else
{