summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSreeharsha Ramanavarapu <sreeharsha.ramanavarapu@oracle.com>2017-11-16 09:31:12 +0530
committerSreeharsha Ramanavarapu <sreeharsha.ramanavarapu@oracle.com>2017-11-16 09:31:12 +0530
commitf06443ce5f9ca8a65b60b595b2095f76d8f65206 (patch)
treefb32f31b289023df647df574d0f094f8c90cea98
parent02c12999f9c8621619f61b10deaba36c0df4dbb7 (diff)
downloadmariadb-git-f06443ce5f9ca8a65b60b595b2095f76d8f65206.tar.gz
Bug #26881946: INCORRECT BEHAVIOR WITH "VALUES"
Issue: ------ VALUES doesn't have a type() function and is considered a Item_field. Solution for 5.7: ----------------- Add a new type() function for Item_values_insert. On 8.0 and trunk it was fixed by Mithun's Bug#19601973. Solution for 5.6: ----------------- Additionally Bug#17458914 is backported. This will address the problem of using VALUES() in INSERT ... ON DUPLICATE KEY UPDATE. Create a field object only if it is in the UPDATE clause, else return a NULL item. This will also address the problems mentioned in Bug#14789787 and Bug#16756402. Solution for 5.5: ----------------- As mentioned above Bug#17458914 is backported. Additionally Bug#14786324 is also backported. When VALUES() is detected outside its meaningful place, it should be treated as NULL and is thus replaced with a Field_null object, with the same name as the original field. Fields with type NULL are generally not handled well inside the server (e.g Innodb will not accept them and it is impossible to create them in regular tables). So create a new const NULL item instead.
-rw-r--r--mysql-test/r/insert_update.result6
-rw-r--r--sql/item.cc27
-rw-r--r--sql/item.h2
-rw-r--r--sql/sql_insert.cc8
-rw-r--r--sql/sql_lex.cc5
-rw-r--r--sql/sql_lex.h4
6 files changed, 32 insertions, 20 deletions
diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result
index a285810b78b..31e7b253267 100644
--- a/mysql-test/r/insert_update.result
+++ b/mysql-test/r/insert_update.result
@@ -60,12 +60,12 @@ explain extended SELECT *, VALUES(a) FROM t1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,values(`test`.`t1`.`a`) AS `VALUES(a)` from `test`.`t1`
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,NULL AS `VALUES(a)` from `test`.`t1`
explain extended select * from t1 where values(a);
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where values(`test`.`t1`.`a`)
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where 0
DROP TABLE t1;
create table t1(a int primary key, b int);
insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,5);
diff --git a/sql/item.cc b/sql/item.cc
index 6f1fdaae398..a37a61453e8 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -7120,7 +7120,7 @@ Item *Item_default_value::transform(Item_transformer transformer, uchar *args)
bool Item_insert_value::eq(const Item *item, bool binary_cmp) const
{
return item->type() == INSERT_VALUE_ITEM &&
- ((Item_default_value *)item)->arg->eq(arg, binary_cmp);
+ ((Item_insert_value *)item)->arg->eq(arg, binary_cmp);
}
@@ -7149,11 +7149,12 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
Item_field *field_arg= (Item_field *)arg;
- if (field_arg->field->table->insert_values)
+ if (field_arg->field->table->insert_values &&
+ thd->lex->in_update_value_clause)
{
Field *def_field= (Field*) sql_alloc(field_arg->field->size_of());
if (!def_field)
- return TRUE;
+ return true;
memcpy(def_field, field_arg->field, field_arg->field->size_of());
def_field->move_field_offset((my_ptrdiff_t)
(def_field->table->insert_values -
@@ -7162,17 +7163,17 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
}
else
{
- Field *tmp_field= field_arg->field;
- /* charset doesn't matter here, it's to avoid sigsegv only */
- tmp_field= new Field_null(0, 0, Field::NONE, field_arg->field->field_name,
- &my_charset_bin);
- if (tmp_field)
- {
- tmp_field->init(field_arg->field->table);
- set_field(tmp_field);
- }
+ // VALUES() is used out-of-scope - its value is always NULL
+ Query_arena backup;
+ Query_arena *const arena= thd->activate_stmt_arena_if_needed(&backup);
+ Item *const item= new Item_null(this->name);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ if (!item)
+ return TRUE;
+ *items= item;
}
- return FALSE;
+ return false;
}
void Item_insert_value::print(String *str, enum_query_type query_type)
diff --git a/sql/item.h b/sql/item.h
index 9f4e1d24424..3556a58ff82 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3150,6 +3150,8 @@ public:
:Item_field(context_arg, (const char *)NULL, (const char *)NULL,
(const char *)NULL),
arg(a) {}
+
+ enum Type type() const { return INSERT_VALUE_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
virtual void print(String *str, enum_query_type query_type);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index dc7cb698476..5d1905bf4ba 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2000, 2017, 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
@@ -1393,9 +1393,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
thd->abort_on_warning= saved_abort_on_warning;
}
+ thd->lex->in_update_value_clause= true;
if (!res)
res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0);
+ thd->lex->in_update_value_clause= false;
+
if (!res && duplic == DUP_UPDATE)
{
select_lex->no_wrap_view_item= TRUE;
@@ -3263,8 +3266,11 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table_list->next_name_resolution_table=
ctx_state.get_first_name_resolution_table();
+ thd->lex->in_update_value_clause= true;
res= res || setup_fields(thd, 0, *info.update_values,
MARK_COLUMNS_READ, 0, 0);
+
+ thd->lex->in_update_value_clause= false;
if (!res)
{
/*
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 50cdabe341a..f7e9fd8570d 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2000, 2017, 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
@@ -2376,7 +2376,8 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
- :result(0), option_type(OPT_DEFAULT), is_lex_started(0)
+ :result(0), option_type(OPT_DEFAULT), is_lex_started(0),
+ in_update_value_clause(false)
{
my_init_dynamic_array2(&plugins, sizeof(plugin_ref),
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 867997feb39..3c950ebe117 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2017, 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
@@ -2491,6 +2491,8 @@ struct LEX: public Query_tables_list
bool escape_used;
bool is_lex_started; /* If lex_start() did run. For debugging. */
+ /// Set to true while resolving values in ON DUPLICATE KEY UPDATE clause
+ bool in_update_value_clause;
/*
The set of those tables whose fields are referenced in all subqueries