Commit 11588e5e authored by evgen@moonbone.local's avatar evgen@moonbone.local

Bug#25122: Views based on a self-joined table aren't insertable.

When INSERT is done over a view the table being inserted into is 
checked to be unique among all views tables. But if the view contains
self-joined table an error will be thrown even if all tables are used under
different aliases.

The unique_table() function now also checks tables' aliases when needed.
parent 8f52c5b2
...@@ -1064,7 +1064,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, ...@@ -1064,7 +1064,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
st_table_list *TABLE_LIST::*link, st_table_list *TABLE_LIST::*link,
const char *db_name, const char *db_name,
const char *table_name); const char *table_name);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
bool check_alias);
TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
bool close_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name);
void close_temporary(TABLE *table, bool delete_table); void close_temporary(TABLE *table, bool delete_table);
......
...@@ -796,6 +796,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, ...@@ -796,6 +796,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
thd thread handle thd thread handle
table table which should be checked table table which should be checked
table_list list of tables table_list list of tables
check_alias whether to check tables' aliases
NOTE: to exclude derived tables from check we use following mechanism: NOTE: to exclude derived tables from check we use following mechanism:
a) during derived table processing set THD::derived_tables_processing a) during derived table processing set THD::derived_tables_processing
...@@ -823,10 +824,11 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, ...@@ -823,10 +824,11 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
0 if table is unique 0 if table is unique
*/ */
TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
bool check_alias)
{ {
TABLE_LIST *res; TABLE_LIST *res;
const char *d_name, *t_name; const char *d_name, *t_name, *t_alias;
DBUG_ENTER("unique_table"); DBUG_ENTER("unique_table");
DBUG_PRINT("enter", ("table alias: %s", table->alias)); DBUG_PRINT("enter", ("table alias: %s", table->alias));
...@@ -854,6 +856,7 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) ...@@ -854,6 +856,7 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list)
} }
d_name= table->db; d_name= table->db;
t_name= table->table_name; t_name= table->table_name;
t_alias= table->alias;
DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name)); DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name));
for (;;) for (;;)
...@@ -861,6 +864,8 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) ...@@ -861,6 +864,8 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list)
if (((! (res= find_table_in_global_list(table_list, d_name, t_name))) && if (((! (res= find_table_in_global_list(table_list, d_name, t_name))) &&
(! (res= mysql_lock_have_duplicate(thd, table, table_list)))) || (! (res= mysql_lock_have_duplicate(thd, table, table_list)))) ||
((!res->table || res->table != table->table) && ((!res->table || res->table != table->table) &&
(!check_alias || !(lower_case_table_names ?
strcasecmp(t_alias, res->alias) : strcmp(t_alias, res->alias))) &&
res->select_lex && !res->select_lex->exclude_from_table_unique_test && res->select_lex && !res->select_lex->exclude_from_table_unique_test &&
!res->prelocking_placeholder)) !res->prelocking_placeholder))
break; break;
......
...@@ -370,7 +370,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) ...@@ -370,7 +370,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
} }
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, table_list, table_list->next_global))) if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
{ {
update_non_unique_table_error(table_list, "DELETE", duplicate); update_non_unique_table_error(table_list, "DELETE", duplicate);
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -462,7 +462,7 @@ bool mysql_multi_delete_prepare(THD *thd) ...@@ -462,7 +462,7 @@ bool mysql_multi_delete_prepare(THD *thd)
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, target_tbl->correspondent_table, if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
lex->query_tables))) lex->query_tables, 0)))
{ {
update_non_unique_table_error(target_tbl->correspondent_table, update_non_unique_table_error(target_tbl->correspondent_table,
"DELETE", duplicate); "DELETE", duplicate);
......
...@@ -1044,7 +1044,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -1044,7 +1044,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
{ {
Item *fake_conds= 0; Item *fake_conds= 0;
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, table_list, table_list->next_global))) if ((duplicate= unique_table(thd, table_list, table_list->next_global, 1)))
{ {
update_non_unique_table_error(table_list, "INSERT", duplicate); update_non_unique_table_error(table_list, "INSERT", duplicate);
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -2424,7 +2424,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -2424,7 +2424,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
query query
*/ */
if (!(lex->current_select->options & OPTION_BUFFER_RESULT) && if (!(lex->current_select->options & OPTION_BUFFER_RESULT) &&
unique_table(thd, table_list, table_list->next_global)) unique_table(thd, table_list, table_list->next_global, 0))
{ {
/* Using same table for INSERT and SELECT */ /* Using same table for INSERT and SELECT */
lex->current_select->options|= OPTION_BUFFER_RESULT; lex->current_select->options|= OPTION_BUFFER_RESULT;
......
...@@ -175,7 +175,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -175,7 +175,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table is marked to be 'used for insert' in which case we should never table is marked to be 'used for insert' in which case we should never
mark this table as as 'const table' (ie, one that has only one row). mark this table as as 'const table' (ie, one that has only one row).
*/ */
if (unique_table(thd, table_list, table_list->next_global)) if (unique_table(thd, table_list, table_list->next_global, 0))
{ {
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
......
...@@ -2993,7 +2993,7 @@ mysql_execute_command(THD *thd) ...@@ -2993,7 +2993,7 @@ mysql_execute_command(THD *thd)
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, create_table, select_tables))) if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
{ {
update_non_unique_table_error(create_table, "CREATE", duplicate); update_non_unique_table_error(create_table, "CREATE", duplicate);
res= 1; res= 1;
...@@ -3009,7 +3009,7 @@ mysql_execute_command(THD *thd) ...@@ -3009,7 +3009,7 @@ mysql_execute_command(THD *thd)
tab= tab->next_local) tab= tab->next_local)
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, tab, select_tables))) if ((duplicate= unique_table(thd, tab, select_tables, 0)))
{ {
update_non_unique_table_error(tab, "CREATE", duplicate); update_non_unique_table_error(tab, "CREATE", duplicate);
res= 1; res= 1;
......
...@@ -636,7 +636,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, ...@@ -636,7 +636,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
/* Check that we are not using table that we are updating in a sub select */ /* Check that we are not using table that we are updating in a sub select */
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, table_list, table_list->next_global))) if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
{ {
update_non_unique_table_error(table_list, "UPDATE", duplicate); update_non_unique_table_error(table_list, "UPDATE", duplicate);
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
...@@ -863,7 +863,7 @@ bool mysql_multi_update_prepare(THD *thd) ...@@ -863,7 +863,7 @@ bool mysql_multi_update_prepare(THD *thd)
tl->lock_type != TL_READ_NO_INSERT) tl->lock_type != TL_READ_NO_INSERT)
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, tl, table_list))) if ((duplicate= unique_table(thd, tl, table_list, 0)))
{ {
update_non_unique_table_error(table_list, "UPDATE", duplicate); update_non_unique_table_error(table_list, "UPDATE", duplicate);
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -1082,7 +1082,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, ...@@ -1082,7 +1082,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab,
List<Item> *fields) List<Item> *fields)
{ {
TABLE *table= join_tab->table; TABLE *table= join_tab->table;
if (unique_table(thd, table_ref, all_tables)) if (unique_table(thd, table_ref, all_tables, 0))
return 0; return 0;
switch (join_tab->type) { switch (join_tab->type) {
case JT_SYSTEM: case JT_SYSTEM:
......
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