1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-32401 expression cache lead to crash if table of wrong type created and the cache switched off

1) take into account TMP_TABLE_ALL_COLUMNS when
   we are modifying agg_item->result_field
2) remove unused now "bool materialized_subquery;"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Oleksandr Byelkin
2026-10-08 16:13:36 +02:00
committed by Oleksandr "Sanja" Byelkin
parent e03abdfec3
commit 155a970264
7 changed files with 133 additions and 27 deletions

View File

@@ -3888,6 +3888,77 @@ pk b
29 3
drop view v1;
drop table t1,t2,t3,t4;
#
# MDEV-32401 expression cache lead to crash if table of wrong type
# created and the cache switched off
#
CREATE TABLE t0 (c TEXT);
INSERT INTO t0 VALUES (71), (-79);
SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`t0`.`c` AS `d` from `test`.`t0` join `test`.`t0` where <expr_cache><`test`.`t0`.`c`>((/* select#3 */ select ord(<expr_cache><`test`.`t0`.`c`,bit_or(`test`.`t0`.`c`),bit_or(`test`.`t0`.`c`)>((/* select#4 */ select bit_or(`test`.`t0`.`c`)))) from `test`.`t0` group by `test`.`t0`.`c`))
SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select cast(`test`.`t0`.`c` as signed) AS `d` from `test`.`t0` join `test`.`t0` where <expr_cache><cast(`test`.`t0`.`c` as signed)>((/* select#3 */ select ord(<expr_cache><cast(`test`.`t0`.`c` as signed),bit_or(cast(`test`.`t0`.`c` as signed)),bit_or(cast(`test`.`t0`.`c` as signed))>((/* select#4 */ select bit_or(cast(`test`.`t0`.`c` as signed))))) from `test`.`t0` group by cast(`test`.`t0`.`c` as signed)))
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (71), (-79);
SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`t1`.`c` AS `d` from `test`.`t1` join `test`.`t1` where <expr_cache><`test`.`t1`.`c`>((/* select#3 */ select ord(<expr_cache><`test`.`t1`.`c`,bit_or(`test`.`t1`.`c`),bit_or(`test`.`t1`.`c`)>((/* select#4 */ select bit_or(`test`.`t1`.`c`)))) from `test`.`t1` group by `test`.`t1`.`c`))
DROP TABLE t0, t1;
# End of 11.4 tests
SET optimizer_switch=@save_optimizer_switch;
# restore default
set @@optimizer_switch= default;

View File

@@ -1739,7 +1739,36 @@ SELECT * FROM t4 WHERE b NOT IN ( SELECT * FROM v1 );
drop view v1;
drop table t1,t2,t3,t4;
SET optimizer_switch=@save_optimizer_switch;
--echo #
--echo # MDEV-32401 expression cache lead to crash if table of wrong type
--echo # created and the cache switched off
--echo #
--echo # restore default
set @@optimizer_switch= default;
CREATE TABLE t0 (c TEXT);
INSERT INTO t0 VALUES (71), (-79);
SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (71), (-79);
SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
EXPLAIN EXTENDED SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
DROP TABLE t0, t1;
--echo # End of 11.4 tests
SET optimizer_switch=@save_optimizer_switch;

View File

@@ -5329,8 +5329,6 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
if (!(name.str= (char*) thd->memdup(buf, name.length + 1)))
DBUG_RETURN(TRUE);
result_sink->get_tmp_table_param()->materialized_subquery= true;
if (item->substype() == Item_subselect::IN_SUBS &&
(item->get_IN_subquery()->is_jtbm_merged))
{

View File

@@ -4641,7 +4641,6 @@ void TMP_TABLE_PARAM::init()
table_charset= 0;
precomputed_group_by= 0;
bit_fields_as_long= 0;
materialized_subquery= 0;
force_not_null_cols= 0;
skip_create_table= 0;
tmp_name= "temptable"; // Name of temp table on disk

View File

@@ -6803,8 +6803,6 @@ public:
bool using_outer_summary_function;
CHARSET_INFO *table_charset;
bool schema_table;
/* TRUE if the temp table is created for subquery materialization. */
bool materialized_subquery;
/* TRUE if all columns of the table are guaranteed to be non-nullable */
bool force_not_null_cols;
/*
@@ -6832,7 +6830,7 @@ public:
:copy_field(0), group_parts(0),
group_length(0), group_null_parts(0),
using_outer_summary_function(0),
schema_table(0), materialized_subquery(0), force_not_null_cols(0),
schema_table(0), force_not_null_cols(0),
precomputed_group_by(0), group_concat(0),
force_copy_fields(0), bit_fields_as_long(0), skip_create_table(0)
{

View File

@@ -58,6 +58,16 @@
#define OPTION_BIG_SELECTS (1ULL << 9) // THD, user
#define OPTION_LOG_OFF (1ULL << 10) // THD, user
#define OPTION_QUOTE_SHOW_CREATE (1ULL << 11) // THD, user
/*
This flag has two effects:
1. Include all select list columns in the temporary table. Do not skip
constant expressions or expressions that include aggregate functions.
2. For items in select list, do NOT set item->result_field to point to
item's image field in the created temporary table.
Roughly speaking, TMP_TABLE_ALL_COLUMNS is NOT used for temporary tables
that do GROUP BY operation, and used for most other tables.
*/
#define TMP_TABLE_ALL_COLUMNS (1ULL << 12) // SELECT, intern
#define OPTION_WARNINGS (1ULL << 13) // THD, user
#define OPTION_AUTO_IS_NULL (1ULL << 14) // THD, user, binlog

View File

@@ -22326,6 +22326,8 @@ bool Create_tmp_table::add_fields(THD *thd,
if (type == Item::SUM_FUNC_ITEM && !m_group && !m_save_sum_fields)
{ /* Can't calc group yet */
Item_sum *sum_item= (Item_sum *) item;
/* There is actually no need to clear result_field */
DBUG_ASSERT(!sum_item->result_field);
sum_item->result_field=0;
for (uint i= 0 ; i < sum_item->get_arg_count() ; i++)
{
@@ -22412,26 +22414,25 @@ bool Create_tmp_table::add_fields(THD *thd,
{
Item_sum *agg_item= (Item_sum *) item;
/*
Update the result field only if it has never been set, or if the
created temporary table is not to be used for subquery
materialization.
Set agg_item->result_field to point to its image in the temporary
table. Do this only when this temporary table is created without
TMP_TABLE_ALL_COLUMNS flag (and so not_all_columns=true).
The reason is that for subqueries that require
materialization as part of their plan, we create the
'external' temporary table needed for IN execution, after
the 'internal' temporary table needed for grouping. Since
both the external and the internal temporary tables are
created for the same list of SELECT fields of the subquery,
setting 'result_field' for each invocation of
create_tmp_table overrides the previous value of
'result_field'.
In this case, the temporary table will be read by going through the
select list and its item->result_field pointers. The most common
examples of this are GROUP BY operation and buffering for
SQL_BUFFER_RESULT).
The condition below prevents the creation of the external
temp table to override the 'result_field' that was set for
the internal temp table.
Note that SELECT has one "select list" but may be processed with
multiple temporary tables. It is therefore important to only set
result_field to point to the right temporary table.
That's why we assert here that we don't set it twice.
*/
if (!agg_item->result_field || !param->materialized_subquery)
if (not_all_columns)
{
DBUG_ASSERT(!agg_item->result_field);
agg_item->result_field= new_field;
}
}
tmp_from_field++;