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:
@ -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)
|
||||
|
Reference in New Issue
Block a user