1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

MDEV-32086 (part 2) Server crash when inserting from derived table containing insert target table

Get rid of need of matherialization for usual INSERT (cache results in
Item_cache* if needed)

- subqueries in VALUE do not see new records in the table we are
  inserting to
- subqueries in RETIRNING prohibited to use the table we are inserting to
This commit is contained in:
Oleksandr Byelkin
2025-04-18 12:14:23 +02:00
parent 9b313d2de1
commit 4fc9dc84b0
33 changed files with 705 additions and 165 deletions

View File

@ -57,6 +57,7 @@
*/
#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_list.h"
#include "sql_priv.h"
#include "sql_insert.h"
#include "sql_update.h" // compare_record
@ -714,6 +715,8 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
Name_resolution_context_state ctx_state;
SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0;
unsigned char *readbuff= NULL;
List<List_item> insert_values_cache;
bool cache_insert_values= FALSE;
#ifndef EMBEDDED_LIBRARY
char *query= thd->query();
@ -771,7 +774,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
if ((res= mysql_prepare_insert(thd, table_list, fields, values,
update_fields, update_values, duplic,
&unused_conds, FALSE)))
&unused_conds, FALSE, &cache_insert_values)))
{
retval= thd->is_error();
if (res < 0)
@ -1000,8 +1003,42 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
restore_record(table,s->default_values); // Get empty record
thd->reconsider_logging_format_for_iodup(table);
}
if (cache_insert_values)
{
insert_values_cache.empty();
while ((values= its++))
{
List<Item> *caches= new (thd->mem_root) List_item;
List_iterator_fast<Item> iv(*values);
Item *item;
if (caches == 0)
{
error= 1;
goto values_loop_end;
}
caches->empty();
while((item= iv++))
{
Item_cache *cache= item->get_cache(thd);
if (!cache)
{
error= 1;
goto values_loop_end;
}
cache->setup(thd, item);
caches->push_back(cache);
}
insert_values_cache.push_back(caches);
}
its.rewind();
}
do
{
List_iterator_fast<List_item> itc(insert_values_cache);
List_iterator_fast<List_item> *itr;
DBUG_PRINT("info", ("iteration %llu", iteration));
if (iteration && bulk_parameters_set(thd))
{
@ -1009,7 +1046,24 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
goto values_loop_end;
}
while ((values= its++))
if (cache_insert_values)
{
List_item *caches;
while ((caches= itc++))
{
List_iterator_fast<Item> ic(*caches);
Item_cache *cache;
while((cache= (Item_cache*) ic++))
{
cache->cache_value();
}
}
itc.rewind();
itr= &itc;
}
else
itr= &its;
while ((values= (*itr)++))
{
if (fields.elements || !value_count)
{
@ -1112,7 +1166,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
break;
thd->get_stmt_da()->inc_current_row_for_warning();
}
its.rewind();
itr->rewind();
iteration++;
} while (bulk_parameters_iterations(thd));
@ -1601,6 +1655,7 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables)
table_list Global/local table list
where Where clause (for insert ... select)
select_insert TRUE if INSERT ... SELECT statement
cache_insert_values insert's VALUES(...) has to be pre-computed
TODO (in far future)
In cases of:
@ -1622,7 +1677,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
List<Item> &fields, List_item *values,
List<Item> &update_fields, List<Item> &update_values,
enum_duplicates duplic, COND **where,
bool select_insert)
bool select_insert, bool * const cache_insert_values)
{
SELECT_LEX *select_lex= thd->lex->first_select_lex();
Name_resolution_context *context= &select_lex->context;
@ -1712,11 +1767,12 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
/*
Check if we read from the same table we're inserting into.
Queries like INSERT INTO t1 VALUES ((SELECT ... FROM t1...)) are not
allowed.
Queries like INSERT INTO t1 VALUES ((SELECT ... FROM t1...)) have
to pre-compute the VALUES part.
Reading from the same table in the RETURNING clause is not allowed.
INSERT...SELECT is an exception: it will detect this case and use
buffering to handle it correctly.
INSERT...SELECT detects this case in select_insert::prepare and also
uses buffering to handle it correcly.
*/
if (!select_insert)
{
@ -1725,10 +1781,30 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if ((duplicate= unique_table(thd, table_list, table_list->next_global,
CHECK_DUP_ALLOW_DIFFERENT_ALIAS)))
{
update_non_unique_table_error(table_list, "INSERT", duplicate);
DBUG_RETURN(1);
/*
This is INSERT INTO ... VALUES (...) and it must pre-compute the
values to be inserted.
*/
(*cache_insert_values)= true;
}
else
(*cache_insert_values)= false;
select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds);
if ((*cache_insert_values) && thd->lex->has_returning())
{
// Check if the table we're inserting into is also in RETURNING clause
TABLE_LIST *dup=
unique_table_in_insert_returning_subselect(thd, table_list,
thd->lex->returning());
if (dup)
{
if (dup != ERROR_TABLE)
update_non_unique_table_error(table_list, "INSERT", duplicate);
DBUG_RETURN(1);
}
}
}
/*
Only call prepare_for_posistion() if we are not performing a DELAYED
@ -3857,6 +3933,7 @@ int mysql_insert_select_prepare(THD *thd, select_result *sel_res)
int res;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= lex->first_select_lex();
bool cache_insert_values= false;
DBUG_ENTER("mysql_insert_select_prepare");
/*
@ -3867,7 +3944,7 @@ int mysql_insert_select_prepare(THD *thd, select_result *sel_res)
if ((res= mysql_prepare_insert(thd, lex->query_tables, lex->field_list, 0,
lex->update_list, lex->value_list,
lex->duplicates,
&select_lex->where, TRUE)))
&select_lex->where, TRUE, &cache_insert_values)))
DBUG_RETURN(res);
/*
@ -4050,8 +4127,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
Is table which we are changing used somewhere in other parts of
query
*/
if (unique_table(thd, table_list, table_list->next_global,
CHECK_DUP_FOR_INSERT_SELECT))
if (unique_table(thd, table_list, table_list->next_global, 0))
{
/* Using same table for INSERT and SELECT */
lex->current_select->options|= OPTION_BUFFER_RESULT;