Commit d8c9cd70 authored by Chaithra Gopalareddy's avatar Chaithra Gopalareddy

Bug#14261010: ON DUPLICATE KEY UPDATE CRASHES THE SERVER

      
Problem:
Insert with 'on duplicate key update' on a view,
crashes the server.
      
Analysis:
During an insert on to a view, we do the following:
      
For insert fields and values -
1. Resolve insert values.
2. Resolve insert fields.
3. Check if the fields and values are all from a 
   single table of a view in case of INSERT VALUES.
   Do not check the same in case of INSERT SELECT,
   as the values can be read from different table than
   that of the view.
      
For the update fields (if DUP UPDATE is used)
1. Create a name resolution context with 'table_list' only.
2. Resolve update fields in this context.
3. Check if update fields and values are from the same
   table as the insert fields.
4. Get the next name resolution context. Concatinate this
   with the previous one.
5. Resolve update values in this context as we can refer
   to other tables in the values clause.
      
Note that at step 3(of update fields), we check for
'used_tables map' of update values, without resolving them
first. Hence the crash.
      
Fix:
At step 3, do not pass the update values to check if its a
single table view update, as update values can refer other table.
      
Code has been re-organized to function like check_insert_fields.


sql/sql_insert.cc:
  Do not pass update_values as they are not resolved yet.
parent 036787a9
/* /*
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -103,13 +103,12 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); ...@@ -103,13 +103,12 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view);
/* /*
Check that insert/update fields are from the same single table of a view. Check that insert/update fields are from the same single table of a view.
SYNOPSIS @param fields The insert/update fields to be checked.
check_view_single_update() @param values The insert/update values to be checked, NULL if
fields The insert/update fields to be checked. checking is not wanted.
view The view for insert. @param view The view for insert.
map [in/out] The insert table map. @param map [in/out] The insert table map.
DESCRIPTION
This function is called in 2 cases: This function is called in 2 cases:
1. to check insert fields. In this case *map will be set to 0. 1. to check insert fields. In this case *map will be set to 0.
Insert fields are checked to be all from the same single underlying Insert fields are checked to be all from the same single underlying
...@@ -120,9 +119,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); ...@@ -120,9 +119,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view);
the function to check insert fields. Update fields are checked to be the function to check insert fields. Update fields are checked to be
from the same table as the insert fields. from the same table as the insert fields.
RETURN @returns false if success.
0 OK
1 Error
*/ */
bool check_view_single_update(List<Item> &fields, List<Item> *values, bool check_view_single_update(List<Item> &fields, List<Item> *values,
...@@ -174,22 +171,20 @@ error: ...@@ -174,22 +171,20 @@ error:
/* /*
Check if insert fields are correct. Check if insert fields are correct.
SYNOPSIS @param thd The current thread.
check_insert_fields() @param table_list The table we are inserting into (may be view)
thd The current thread. @param fields The insert fields.
table The table for insert. @param values The insert values.
fields The insert fields. @param check_unique If duplicate values should be rejected.
values The insert values. @param fields_and_values_from_different_maps If 'values' are allowed to
check_unique If duplicate values should be rejected. refer to other tables than those of 'fields'
@param map See check_view_single_update
NOTE NOTE
Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
or leaves it as is, depending on if timestamp should be updated or or leaves it as is, depending on if timestamp should be updated or
not. not.
RETURN @returns 0 if success, -1 if error
0 OK
-1 Error
*/ */
static int check_insert_fields(THD *thd, TABLE_LIST *table_list, static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
...@@ -312,28 +307,29 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, ...@@ -312,28 +307,29 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
} }
/* /**
Check update fields for the timestamp field. Check if update fields are correct.
SYNOPSIS @param thd The current thread.
check_update_fields() @param insert_table_list The table we are inserting into (may be view)
thd The current thread. @param update_fields The update fields.
insert_table_list The insert table list. @param update_values The update values.
table The table for update. @param fields_and_values_from_different_maps If 'update_values' are allowed to
update_fields The update fields. refer to other tables than those of 'update_fields'
@param map See check_view_single_update
NOTE NOTE
If the update fields include the timestamp field, If the update fields include the timestamp field,
remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type. remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
RETURN @returns 0 if success, -1 if error
0 OK
-1 Error
*/ */
static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
List<Item> &update_fields, List<Item> &update_fields,
List<Item> &update_values, table_map *map) List<Item> &update_values,
bool fields_and_values_from_different_maps,
table_map *map)
{ {
TABLE *table= insert_table_list->table; TABLE *table= insert_table_list->table;
my_bool timestamp_mark= 0; my_bool timestamp_mark= 0;
...@@ -353,7 +349,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, ...@@ -353,7 +349,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
return -1; return -1;
if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE &&
check_view_single_update(update_fields, &update_values, check_view_single_update(update_fields,
fields_and_values_from_different_maps ?
(List<Item>*) 0 : &update_values,
insert_table_list, map)) insert_table_list, map))
return -1; return -1;
...@@ -1402,7 +1400,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -1402,7 +1400,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
{ {
select_lex->no_wrap_view_item= TRUE; select_lex->no_wrap_view_item= TRUE;
res= check_update_fields(thd, context->table_list, update_fields, res= check_update_fields(thd, context->table_list, update_fields,
update_values, &map); update_values, false, &map);
select_lex->no_wrap_view_item= FALSE; select_lex->no_wrap_view_item= FALSE;
} }
...@@ -3212,8 +3210,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -3212,8 +3210,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
context->resolve_in_table_list_only(table_list); context->resolve_in_table_list_only(table_list);
lex->select_lex.no_wrap_view_item= TRUE; lex->select_lex.no_wrap_view_item= TRUE;
res= res || check_update_fields(thd, context->table_list, res= res ||
check_update_fields(thd, context->table_list,
*info.update_fields, *info.update_values, *info.update_fields, *info.update_values,
/*
In INSERT SELECT ON DUPLICATE KEY UPDATE col=x
'x' can legally refer to a non-inserted table.
'x' is not even resolved yet.
*/
true,
&map); &map);
lex->select_lex.no_wrap_view_item= FALSE; lex->select_lex.no_wrap_view_item= FALSE;
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment