Skip to content

Commit fcc914f

Browse files
committed
Bug#16820562 Bad column names are not rejected at 'prepare', only at 'execute'
Based on a patch by Prabakaran Thirumalai. INSERT...SELECT statement reports non-existing column names during execution phase rather than in the preparation phase, whereas INSERT...VALUES reports these issues during preparation phase. The function mysql_prepare_insert() resolves columns and values for ON DUPLICATE KEY UPDATE only when VALUES clause is present. With SELECT, these lists are resolved by select_insert::prepare(), but this function is not called during statement preparation, only during execution. Fixed by letting mysql_prepare_insert() resolve these lists during preparation. An unfortunate effect of this is that some resolver actions for INSERT statements are now done at three different places. We hope to be able to refactor this code later so that the lists can be resolved at a single place.
1 parent 1f58276 commit fcc914f

File tree

5 files changed

+114
-35
lines changed

5 files changed

+114
-35
lines changed

mysql-test/r/insert.result

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,6 @@ insert into t1 SET a=1, b=a+1;
306306
insert into t1 (a,b) select 1,2;
307307
INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a);
308308
prepare stmt1 from ' replace into t1 (a,a) select 100, ''hundred'' ';
309-
execute stmt1;
310309
ERROR 42000: Column 'a' specified twice
311310
insert into t1 (a,b,b) values (1,1,1);
312311
ERROR 42000: Column 'b' specified twice

mysql-test/r/ps.result

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,17 +1731,9 @@ drop tables if exists t1;
17311731
create table t1 (id int primary key auto_increment, value varchar(10));
17321732
insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD');
17331733
prepare stmt from "insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'";
1734-
execute stmt;
1735-
ERROR 42S22: Unknown column 'v' in 'field list'
1736-
execute stmt;
17371734
ERROR 42S22: Unknown column 'v' in 'field list'
1738-
deallocate prepare stmt;
17391735
prepare stmt from "insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'";
1740-
execute stmt;
17411736
ERROR 42S22: Unknown column 'y.value' in 'field list'
1742-
execute stmt;
1743-
ERROR 42S22: Unknown column 'y.value' in 'field list'
1744-
deallocate prepare stmt;
17451737
drop tables t1;
17461738
prepare stmt from "create table t1 select ?";
17471739
set @a=1.0;
@@ -3937,3 +3929,21 @@ field1
39373929
150
39383930
DEALLOCATE PREPARE stmt;
39393931
DROP TABLE t1, t2;
3932+
#
3933+
# Bug#16820562: Bad column names are not rejected at 'prepare'
3934+
#
3935+
Resolver errors should be given at prepare time in insert select
3936+
CREATE TABLE t1 (a INTEGER);
3937+
PREPARE s FROM "INSERT INTO t1 VALUES(1) ON DUPLICATE KEY UPDATE absent=2";
3938+
ERROR 42S22: Unknown column 'absent' in 'field list'
3939+
PREPARE s FROM "INSERT INTO t1 VALUES(1) ON DUPLICATE KEY UPDATE a=absent";
3940+
ERROR 42S22: Unknown column 'absent' in 'field list'
3941+
PREPARE s FROM "INSERT INTO t1 SELECT 1 ON DUPLICATE KEY UPDATE absent=2";
3942+
ERROR 42S22: Unknown column 'absent' in 'field list'
3943+
PREPARE s FROM "INSERT INTO t1 SELECT 1 ON DUPLICATE KEY UPDATE a=absent";
3944+
ERROR 42S22: Unknown column 'absent' in 'field list'
3945+
PREPARE s FROM "INSERT INTO t1(absent) VALUES(1) ON DUPLICATE KEY UPDATE a=1";
3946+
ERROR 42S22: Unknown column 'absent' in 'field list'
3947+
PREPARE s FROM "INSERT INTO t1(absent) SELECT 1 ON DUPLICATE KEY UPDATE a=1";
3948+
ERROR 42S22: Unknown column 'absent' in 'field list'
3949+
DROP TABLE t1;

mysql-test/t/insert.test

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,8 @@ insert into t1 (a,b) values (a,b);
188188
insert into t1 SET a=1, b=a+1;
189189
insert into t1 (a,b) select 1,2;
190190
INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a);
191-
prepare stmt1 from ' replace into t1 (a,a) select 100, ''hundred'' ';
192191
--error 1110
193-
execute stmt1;
192+
prepare stmt1 from ' replace into t1 (a,a) select 100, ''hundred'' ';
194193
--error 1110
195194
insert into t1 (a,b,b) values (1,1,1);
196195
--error 1136

mysql-test/t/ps.test

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,24 +1798,17 @@ set @@character_set_server= @old_character_set_server;
17981798
drop tables if exists t1;
17991799
create table t1 (id int primary key auto_increment, value varchar(10));
18001800
insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD');
1801-
# Let us prepare INSERT ... SELECT ... ON DUPLICATE KEY UPDATE statement
1801+
# Prepare INSERT ... SELECT ... ON DUPLICATE KEY UPDATE statement
18021802
# which in its ON DUPLICATE KEY clause erroneously tries to assign value
1803-
# to a column which is mentioned only in SELECT part.
1804-
prepare stmt from "insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'";
1805-
# Both first and second attempts to execute it should fail
1806-
--error ER_BAD_FIELD_ERROR
1807-
execute stmt;
1803+
# to a column which is mentioned only in SELECT part. This is now caught
1804+
# during preparation.
18081805
--error ER_BAD_FIELD_ERROR
1809-
execute stmt;
1810-
deallocate prepare stmt;
1806+
prepare stmt from "insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'";
1807+
#
18111808
# And now the same test for more complex case which is more close
18121809
# to the one that was reported originally.
1813-
prepare stmt from "insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'";
1814-
--error ER_BAD_FIELD_ERROR
1815-
execute stmt;
18161810
--error ER_BAD_FIELD_ERROR
1817-
execute stmt;
1818-
deallocate prepare stmt;
1811+
prepare stmt from "insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'";
18191812
drop tables t1;
18201813

18211814
#
@@ -3486,4 +3479,26 @@ DEALLOCATE PREPARE stmt;
34863479

34873480
DROP TABLE t1, t2;
34883481

3489-
###########################################################################
3482+
--echo #
3483+
--echo # Bug#16820562: Bad column names are not rejected at 'prepare'
3484+
--echo #
3485+
3486+
--echo Resolver errors should be given at prepare time in insert select
3487+
3488+
CREATE TABLE t1 (a INTEGER);
3489+
3490+
--error ER_BAD_FIELD_ERROR
3491+
PREPARE s FROM "INSERT INTO t1 VALUES(1) ON DUPLICATE KEY UPDATE absent=2";
3492+
--error ER_BAD_FIELD_ERROR
3493+
PREPARE s FROM "INSERT INTO t1 VALUES(1) ON DUPLICATE KEY UPDATE a=absent";
3494+
--error ER_BAD_FIELD_ERROR
3495+
PREPARE s FROM "INSERT INTO t1 SELECT 1 ON DUPLICATE KEY UPDATE absent=2";
3496+
--error ER_BAD_FIELD_ERROR
3497+
PREPARE s FROM "INSERT INTO t1 SELECT 1 ON DUPLICATE KEY UPDATE a=absent";
3498+
3499+
--error ER_BAD_FIELD_ERROR
3500+
PREPARE s FROM "INSERT INTO t1(absent) VALUES(1) ON DUPLICATE KEY UPDATE a=1";
3501+
--error ER_BAD_FIELD_ERROR
3502+
PREPARE s FROM "INSERT INTO t1(absent) SELECT 1 ON DUPLICATE KEY UPDATE a=1";
3503+
3504+
DROP TABLE t1;

sql/sql_insert.cc

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ static bool check_view_single_update(List<Item> &fields, TABLE_LIST *view,
9696
@param table_list The table for insert.
9797
@param fields The insert fields.
9898
@param value_count Number of values supplied
99+
= 0: INSERT ... SELECT, delay field count check
99100
@param check_unique If duplicate values should be rejected.
100101
@param[out] insert_table_ref resolved reference to base table
101102
@@ -113,15 +114,15 @@ static bool check_insert_fields(THD *thd, TABLE_LIST *table_list,
113114

114115
DBUG_ASSERT(table_list->updatable);
115116

116-
if (fields.elements == 0 && value_count != 0)
117+
if (fields.elements == 0)
117118
{
118119
// No field list supplied, use field list of table being updated.
119120

120121
DBUG_ASSERT(table); // This branch is not reached with a view:
121122

122123
*insert_table_ref= table_list;
123124

124-
if (value_count != table->s->fields)
125+
if (value_count > 0 && value_count != table->s->fields)
125126
{
126127
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
127128
return true;
@@ -146,7 +147,7 @@ static bool check_insert_fields(THD *thd, TABLE_LIST *table_list,
146147
Name_resolution_context_state ctx_state;
147148
int res;
148149

149-
if (fields.elements != value_count)
150+
if (value_count > 0 && fields.elements != value_count)
150151
{
151152
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
152153
return true;
@@ -1153,15 +1154,15 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
11531154
if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert))
11541155
DBUG_RETURN(true);
11551156

1157+
// Save the state of the current name resolution context.
1158+
ctx_state.save_state(context, table_list);
1159+
11561160
// Prepare the lists of columns and values in the statement.
11571161
if (values)
11581162
{
1159-
/* if we have INSERT ... VALUES () we cannot have a GROUP BY clause */
1163+
// if we have INSERT ... VALUES () we cannot have a GROUP BY clause
11601164
DBUG_ASSERT (!select_lex->group_list.elements);
11611165

1162-
/* Save the state of the current name resolution context. */
1163-
ctx_state.save_state(context, table_list);
1164-
11651166
/*
11661167
Perform name resolution only in the first table - 'table_list',
11671168
which is the table that is inserted into.
@@ -1192,18 +1193,73 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
11921193
if (!res && duplic == DUP_UPDATE)
11931194
{
11941195
select_lex->no_wrap_view_item= true;
1195-
// Setup the fields to be modified
1196+
// Resolve the columns that will be updated
11961197
res= setup_fields(thd, Ref_ptr_array(),
11971198
update_fields, MARK_COLUMNS_WRITE, 0, 0);
11981199
select_lex->no_wrap_view_item= false;
11991200
if (!res)
12001201
res= check_valid_table_refs(table_list, update_fields, map);
12011202
}
1203+
}
1204+
else if (thd->stmt_arena->is_stmt_prepare())
1205+
{
1206+
/*
1207+
This section of code is more or less a duplicate of the code in
1208+
select_insert::prepare, and the 'if' branch above.
1209+
@todo Consolidate these three sections into one.
1210+
*/
1211+
/*
1212+
Perform name resolution only in the first table - 'table_list',
1213+
which is the table that is inserted into.
1214+
*/
1215+
table_list->next_local= NULL;
1216+
thd->dup_field= NULL;
1217+
context->resolve_in_table_list_only(table_list);
12021218

1203-
/* Restore the current context. */
1204-
ctx_state.restore_state(context, table_list);
1219+
res= check_insert_fields(thd, context->table_list, fields, 0,
1220+
!insert_into_view, insert_table_ref);
1221+
table_map map= 0;
1222+
if (!res)
1223+
map= (*insert_table_ref)->table->map;
1224+
1225+
if (!res && duplic == DUP_UPDATE)
1226+
{
1227+
select_lex->no_wrap_view_item= true;
1228+
1229+
// Resolve the columns that will be updated
1230+
res= setup_fields(thd, Ref_ptr_array(),
1231+
update_fields, MARK_COLUMNS_WRITE, 0, 0);
1232+
select_lex->no_wrap_view_item= false;
1233+
if (!res)
1234+
res= check_valid_table_refs(table_list, update_fields, map);
1235+
1236+
DBUG_ASSERT(!table_list->next_name_resolution_table);
1237+
if (select_lex->group_list.elements == 0 && !select_lex->with_sum_func)
1238+
{
1239+
/*
1240+
There are two separata name resolution contexts:
1241+
the INSERT table and the tables in the SELECT expression
1242+
Make a single context out of them by concatenating the lists:
1243+
*/
1244+
table_list->next_name_resolution_table=
1245+
ctx_state.get_first_name_resolution_table();
1246+
}
1247+
thd->lex->in_update_value_clause= true;
1248+
if (!res)
1249+
res= setup_fields(thd, Ref_ptr_array(), update_values,
1250+
MARK_COLUMNS_READ, 0, 0);
1251+
thd->lex->in_update_value_clause= false;
1252+
1253+
/*
1254+
Notice that there is no need to apply the Item::update_value_transformer
1255+
here, as this will be done during EXECUTE in select_insert::prepare().
1256+
*/
1257+
}
12051258
}
12061259

1260+
// Restore the current name resolution context
1261+
ctx_state.restore_state(context, table_list);
1262+
12071263
if (res)
12081264
DBUG_RETURN(res);
12091265

0 commit comments

Comments
 (0)