MDEV-28615 Crash caused by multi-table UPDATE over derived with hanging CTE

This bug affected only multi-table update statements and in very rare
cases: one of the tables used at the top level of the statement must be
a derived table containg a row construct with a subquery including hanging
CTE.

Before this patch was applied the function prepare_unreferenced() of the
class With_element when invoked for the the hangin CTE did not properly
restored the value of thd->lex->context_analysis_only. As a result it
became 0 after the call of this function.
For a query affected by the bug this function is called when
JOIN::prepare() is called for the subquery with a hanging CTE. This happens
when Item_row::fix_fields() calls fix_fields() for the subquery. Setting
the value of thd->lex->context_analysis_only forces the caller function
Item_row::fix_fields() to invoke the virtual method is_null() for the
subquery that leads to execution of it. It causes an assertion failure
because the call of Item_row::fix_fields() happens during the invocation
of Multiupdate_prelocking_strategy::handle_end() that calls the function
mysql_derived_prepare() for the derived table used by the UPDATE at the
time when proper locks for the statement tables has not been acquired yet.

With this patch the value of thd->lex->context_analysis_only is restored
to CONTEXT_ANALYSIS_ONLY_DERIVED that is set in the function
mysql_multi_update_prepare().

Approved by Oleksandr Byelkin <sanja@mariadb.com>
This commit is contained in:
Igor Babaev 2023-10-27 08:53:30 -07:00
commit 9e321a44ee
3 changed files with 54 additions and 4 deletions

View file

@ -1784,8 +1784,6 @@ with data as (select 1 as id)
select id into @myid from data;
set sql_mode= @save_sql_mode;
--echo #
--echo # MDEV-31995 CTE column name specification inconsistency
--echo #
@ -1942,4 +1940,31 @@ drop procedure sp;
drop table distances, city_population;
--echo #
--echo # MDEV-28615: Multi-table UPDATE over derived table containing
--echo # row that uses subquery with hanging CTE
--echo #
CREATE TABLE t1 (a int) ENGINE=MYISAM;
INSERT INTO t1 VALUES (3), (7), (1);
--error ER_OPERAND_COLUMNS
UPDATE
(SELECT (5, (WITH cte AS (SELECT 1) SELECT a FROM t1))) dt
JOIN t1 t
ON t.a=dt.a
SET t.a = 1;
UPDATE
(SELECT a FROM t1
WHERE (5, (WITH cte AS (SELECT 1) SELECT a FROM t1 WHERE a > 4)) <=
(5,a)) dt
JOIN t1 t
ON t.a=dt.a
SET t.a = 1;
SELECT * FROM t1;
DROP TABLE t1;
--echo # End of 10.4 tests