summaryrefslogtreecommitdiff
path: root/sql/sql_insert.cc
diff options
context:
space:
mode:
authorChaithra Gopalareddy <chaithra.gopalareddy@oracle.com>2013-03-30 19:24:54 +0530
committerChaithra Gopalareddy <chaithra.gopalareddy@oracle.com>2013-03-30 19:24:54 +0530
commit4a3708a4f6293011756e29c922f5b74cc0196cb2 (patch)
treedbef16ea37e80b807272e32c8f3beb0d2f6c7e19 /sql/sql_insert.cc
parent7fbd770c7c2378fba613c276d99f4bb74fb1b177 (diff)
downloadmariadb-git-4a3708a4f6293011756e29c922f5b74cc0196cb2.tar.gz
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.
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r--sql/sql_insert.cc85
1 files changed, 45 insertions, 40 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index f47f1418b4b..94f4403d02e 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,5 +1,5 @@
/*
- 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
it under the terms of the GNU General Public License as published by
@@ -103,14 +103,13 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view);
/*
Check that insert/update fields are from the same single table of a view.
- SYNOPSIS
- check_view_single_update()
- fields The insert/update fields to be checked.
- view The view for insert.
- map [in/out] The insert table map.
+ @param fields The insert/update fields to be checked.
+ @param values The insert/update values to be checked, NULL if
+ checking is not wanted.
+ @param view The view for insert.
+ @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.
Insert fields are checked to be all from the same single underlying
table of the given view. Otherwise the error is thrown. Found table
@@ -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
from the same table as the insert fields.
- RETURN
- 0 OK
- 1 Error
+ @returns false if success.
*/
bool check_view_single_update(List<Item> &fields, List<Item> *values,
@@ -174,22 +171,20 @@ error:
/*
Check if insert fields are correct.
- SYNOPSIS
- check_insert_fields()
- thd The current thread.
- table The table for insert.
- fields The insert fields.
- values The insert values.
- check_unique If duplicate values should be rejected.
-
+ @param thd The current thread.
+ @param table_list The table we are inserting into (may be view)
+ @param fields The insert fields.
+ @param values The insert values.
+ @param check_unique If duplicate values should be rejected.
+ @param fields_and_values_from_different_maps If 'values' are allowed to
+ refer to other tables than those of 'fields'
+ @param map See check_view_single_update
NOTE
Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
or leaves it as is, depending on if timestamp should be updated or
not.
-
- RETURN
- 0 OK
- -1 Error
+
+ @returns 0 if success, -1 if error
*/
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
- check_update_fields()
- thd The current thread.
- insert_table_list The insert table list.
- table The table for update.
- update_fields The update fields.
+ @param thd The current thread.
+ @param insert_table_list The table we are inserting into (may be view)
+ @param update_fields The update fields.
+ @param update_values The update values.
+ @param fields_and_values_from_different_maps If 'update_values' are allowed to
+ refer to other tables than those of 'update_fields'
+ @param map See check_view_single_update
NOTE
If the update fields include the timestamp field,
remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
- RETURN
- 0 OK
- -1 Error
+ @returns 0 if success, -1 if error
*/
static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
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;
my_bool timestamp_mark= 0;
@@ -353,7 +349,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
return -1;
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))
return -1;
@@ -1402,7 +1400,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
{
select_lex->no_wrap_view_item= TRUE;
res= check_update_fields(thd, context->table_list, update_fields,
- update_values, &map);
+ update_values, false, &map);
select_lex->no_wrap_view_item= FALSE;
}
@@ -3212,9 +3210,16 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
context->resolve_in_table_list_only(table_list);
lex->select_lex.no_wrap_view_item= TRUE;
- res= res || check_update_fields(thd, context->table_list,
- *info.update_fields, *info.update_values,
- &map);
+ res= res ||
+ check_update_fields(thd, context->table_list,
+ *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);
lex->select_lex.no_wrap_view_item= FALSE;
/*
When we are not using GROUP BY and there are no ungrouped aggregate functions