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

MDEV-13417 UPDATE produces wrong values if an updated column is later used as an update source

Standard compatible behavior for UPDATE: all assignments in SET
are executed "simultaneously", not left-to-right. And `SET a=b,b=a`
will swap the values.
This commit is contained in:
Jerome Brauge
2018-01-29 16:53:39 +01:00
committed by Sergei Golubchik
parent 355ee6877b
commit d943d7f712
29 changed files with 566 additions and 76 deletions

View File

@ -132,25 +132,50 @@ bool compare_record(const TABLE *table)
FALSE Items are OK
*/
static bool check_fields(THD *thd, List<Item> &items)
static bool check_fields(THD *thd, List<Item> &items, bool update_view)
{
List_iterator<Item> it(items);
Item *item;
Item_field *field;
while ((item= it++))
if (update_view)
{
if (!(field= item->field_for_view_update()))
List_iterator<Item> it(items);
Item_field *field;
while ((item= it++))
{
/* item has name, because it comes from VIEW SELECT list */
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str);
return TRUE;
if (!(field= item->field_for_view_update()))
{
/* item has name, because it comes from VIEW SELECT list */
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str);
return TRUE;
}
/*
we make temporary copy of Item_field, to avoid influence of changing
result_field on Item_ref which refer on this field
*/
thd->change_item_tree(it.ref(),
new (thd->mem_root) Item_field(thd, field));
}
}
if (thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT)
{
// Make sure that a column is updated only once
List_iterator_fast<Item> it(items);
while ((item= it++))
{
item->field_for_view_update()->field->clear_has_explicit_value();
}
it.rewind();
while ((item= it++))
{
Field *f= item->field_for_view_update()->field;
if (f->has_explicit_value())
{
my_error(ER_UPDATED_COLUMN_ONLY_ONCE, MYF(0),
*(f->table_name), f->field_name.str);
return TRUE;
}
f->set_has_explicit_value();
}
/*
we make temporary copy of Item_field, to avoid influence of changing
result_field on Item_ref which refer on this field
*/
thd->change_item_tree(it.ref(), new (thd->mem_root) Item_field(thd, field));
}
return FALSE;
}
@ -373,7 +398,7 @@ int mysql_update(THD *thd,
if (setup_fields_with_no_wrap(thd, Ref_ptr_array(),
fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
if (table_list->view && check_fields(thd, fields))
if (check_fields(thd, fields, table_list->view))
{
DBUG_RETURN(1);
}
@ -1534,6 +1559,7 @@ int mysql_multi_update_prepare(THD *thd)
//We need to merge for insert prior to prepare.
if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(TRUE);
if (mysql_handle_derived(lex, DT_PREPARE))
DBUG_RETURN(TRUE);
@ -1560,7 +1586,7 @@ int mysql_multi_update_prepare(THD *thd)
}
}
if (update_view && check_fields(thd, *fields))
if (check_fields(thd, *fields, update_view))
{
DBUG_RETURN(TRUE);
}