1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-24710 Uninitialized value upon CREATE .. SELECT ... VALUE...

The failure happened for group by queries when all tables where marked as
'const tables' (tables with 0-1 matching rows) and no row matched the
where clause and there was in addition a direct reference to a field.

In this case the field would not be properly reset and the query would
return 'random data' that happended to be in table->record[0].

Fixed by marking all const tables as null tables in this particular case.

Sergei also provided an extra test case for the code.

@reviewer Sergei Petrunia <psergey@askmonty.org>
This commit is contained in:
Monty
2021-02-27 19:56:46 +02:00
parent 43a0a81397
commit 6983ce704b
4 changed files with 135 additions and 10 deletions

View File

@ -13668,22 +13668,71 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
DBUG_RETURN(0);
}
/*
used only in JOIN::clear
/**
used only in JOIN::clear (always) and in do_select()
(if there where no matching rows)
@param join JOIN
@param cleared_tables If not null, clear also const tables and mark all
cleared tables in the map. cleared_tables is only
set when called from do_select() when there is a
group function and there where no matching rows.
*/
static void clear_tables(JOIN *join)
static void clear_tables(JOIN *join, table_map *cleared_tables)
{
/*
must clear only the non-const tables, as const tables
are not re-calculated.
must clear only the non-const tables as const tables are not re-calculated.
*/
for (uint i= 0 ; i < join->table_count ; i++)
{
if (!(join->table[i]->map & join->const_table_map))
mark_as_null_row(join->table[i]); // All fields are NULL
TABLE *table= join->table[i];
if (table->null_row)
continue; // Nothing more to do
if (!(table->map & join->const_table_map) || cleared_tables)
{
if (cleared_tables)
{
(*cleared_tables)|= (((table_map) 1) << i);
if (table->s->null_bytes)
{
/*
Remember null bits for the record so that we can restore the
original const record in unclear_tables()
*/
memcpy(table->record[1], table->null_flags, table->s->null_bytes);
}
}
mark_as_null_row(table); // All fields are NULL
}
}
}
/**
Reverse null marking for tables and restore null bits.
We have to do this because the tables may be re-used in a sub query
and the subquery will assume that the const tables contains the original
data before clear_tables().
*/
static void unclear_tables(JOIN *join, table_map *cleared_tables)
{
for (uint i= 0 ; i < join->table_count ; i++)
{
if ((*cleared_tables) & (((table_map) 1) << i))
{
TABLE *table= join->table[i];
if (table->s->null_bytes)
memcpy(table->null_flags, table->record[1], table->s->null_bytes);
unmark_as_null_row(table);
}
}
}
/*****************************************************************************
Make som simple condition optimization:
If there is a test 'field = const' change all refs to 'field' to 'const'
@ -19194,6 +19243,7 @@ do_select(JOIN *join, Procedure *procedure)
if (join->only_const_tables() && !join->need_tmp)
{
Next_select_func end_select= setup_end_select_func(join, NULL);
/*
HAVING will be checked after processing aggregate functions,
But WHERE should checked here (we alredy have read tables).
@ -19220,12 +19270,29 @@ do_select(JOIN *join, Procedure *procedure)
}
else if (join->send_row_on_empty_set())
{
table_map cleared_tables= (table_map) 0;
if (end_select == end_send_group)
{
/*
Was a grouping query but we did not find any rows. In this case
we clear all tables to get null in any referenced fields,
like in case of:
SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL
*/
clear_tables(join, &cleared_tables);
}
if (!join->having || join->having->val_int())
{
List<Item> *columns_list= (procedure ? &join->procedure_fields_list :
join->fields);
rc= join->result->send_data(*columns_list) > 0;
}
/*
We have to remove the null markings from the tables as this table
may be part of a sub query that is re-evaluated
*/
if (cleared_tables)
unclear_tables(join, &cleared_tables);
}
/*
An error can happen when evaluating the conds
@ -20197,8 +20264,8 @@ join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos)
if ((table->null_row= MY_TEST((*tab->on_expr_ref)->val_int() == 0)))
mark_as_null_row(table);
}
if (!table->null_row)
table->maybe_null=0;
if (!table->null_row && ! tab->join->mixed_implicit_grouping)
table->maybe_null= 0;
{
JOIN *join= tab->join;
@ -25331,7 +25398,7 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABL
void JOIN::clear()
{
clear_tables(this);
clear_tables(this, 0);
copy_fields(&tmp_table_param);
if (sum_funcs)