Commit 9f9893c9 authored by unknown's avatar unknown

fixed detection of updating table on which we select (BUG#6032)


mysql-test/r/view.result:
  Trys update table from which we select using views and subqueries
mysql-test/t/view.test:
  Trys update table from which we select using views and subqueries
sql/sql_acl.cc:
  fix of fix for bug BUG#5976
sql/sql_base.cc:
  protection against temporary tables which have not table->table->table_cache_key
  fixed unique check to skip the same table instences
sql/sql_delete.cc:
  removed next_independent to allow to check VIEW subqueries
sql/sql_insert.cc:
  removed next_independent to allow to check VIEW subqueries
sql/sql_parse.cc:
  removed next_independent to allow to check VIEW subqueries
sql/sql_update.cc:
  removed next_independent to allow to check VIEW subqueries
sql/sql_view.cc:
  removed next_independent to allow to check VIEW subqueries
  optimisation to mark as non-updatable views with subqueries by same table.
sql/table.h:
  removed next_independent to allow to check VIEW subqueries
parent 4f410c4b
......@@ -1633,3 +1633,16 @@ use mysqltest;
create view v1 as select * from t1;
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
drop database mysqltest;
create table t1 (s1 smallint);
create view v1 as select * from t1 where 20 < (select (s1) from t1);
insert into v1 values (30);
ERROR HY000: The target table v1 of the INSERT is not updatable
create view v2 as select * from t1;
create view v3 as select * from t1 where 20 < (select (s1) from v2);
insert into v3 values (30);
ERROR HY000: The target table v3 of the INSERT is not updatable
create view v4 as select * from v2 where 20 < (select (s1) from t1);
insert into v4 values (30);
ERROR HY000: You can't specify target table 'v4' for update in FROM clause
drop view v4, v3, v2, v1;
drop table t1;
......@@ -1568,7 +1568,23 @@ connection user1;
use mysqltest;
create view v1 as select * from t1;
connection root;
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
drop database mysqltest;
#
# Trys update table from which we select using views and subqueries
#
create table t1 (s1 smallint);
create view v1 as select * from t1 where 20 < (select (s1) from t1);
-- error 1288
insert into v1 values (30);
create view v2 as select * from t1;
create view v3 as select * from t1 where 20 < (select (s1) from v2);
-- error 1288
insert into v3 values (30);
create view v4 as select * from v2 where 20 < (select (s1) from t1);
-- error 1093
insert into v4 values (30);
drop view v4, v3, v2, v1;
drop table t1;
......@@ -3886,13 +3886,19 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table)
{
/* --skip-grants */
if (!initialized)
{
grant->privilege= ~NO_ACCESS; // everything is allowed
return;
}
/* global privileges */
grant->privilege= thd->master_access;
/* db privileges */
grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0);
/* if privileges ignored (--skip-grant-tables) above is enough */
if (!grant_option)
return;
......
......@@ -593,7 +593,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
{
if ((!strcmp(table->db, db_name) &&
!strcmp(table->real_name, table_name)) ||
(table->view &&
(table->view && // it is VIEW and
table->table->table_cache_key && // it is not temporary table
!strcmp(table->table->table_cache_key, db_name) &&
!strcmp(table->table->table_name, table_name)))
break;
......@@ -618,6 +619,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list)
{
DBUG_ENTER("unique_table");
DBUG_PRINT("enter", ("table alias: %s", table->alias));
TABLE_LIST *res;
const char *d_name= table->db, *t_name= table->real_name;
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
......@@ -644,13 +647,18 @@ TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list)
return 0;
}
}
if ((res= find_table_in_global_list(table_list, d_name, t_name)) &&
res->table && res->table == table->table)
DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name));
for(;;)
{
// we found entry of this table try again.
return find_table_in_global_list(res->next_global, d_name, t_name);
if (!(res= find_table_in_global_list(table_list, d_name, t_name)) ||
!res->table || res->table != table->table)
break;
/* if we found entry of this table try again. */
table_list= res->next_global;
DBUG_PRINT("info", ("found same copy of table"));
}
return res;
DBUG_RETURN(res);
}
......
......@@ -293,7 +293,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
DBUG_RETURN(-1);
}
if (unique_table(table_list, table_list->next_independent()))
if (unique_table(table_list, table_list->next_global))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
......
......@@ -641,7 +641,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
setup_fields(thd, 0, table_list, update_values, 0, 0, 0))))
DBUG_RETURN(-1);
if (unique_table(table_list, table_list->next_independent()))
if (unique_table(table_list, table_list->next_global))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
......
......@@ -2810,7 +2810,7 @@ mysql_execute_command(THD *thd)
Is table which we are changing used somewhere in other parts of
query
*/
if (unique_table(first_table, all_tables->next_independent()))
if (unique_table(first_table, all_tables->next_global))
{
/* Using same table for INSERT and SELECT */
select_lex->options |= OPTION_BUFFER_RESULT;
......
......@@ -529,7 +529,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(-1);
/* Check that we are not using table that we are updating in a sub select */
if (unique_table(table_list, table_list->next_independent()))
if (unique_table(table_list, table_list->next_global))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
......
......@@ -382,6 +382,7 @@ static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}};
static int mysql_register_view(THD *thd, TABLE_LIST *view,
enum_view_create_mode mode)
{
LEX *lex= thd->lex;
char buff[4096];
String str(buff,(uint32) sizeof(buff), system_charset_info);
char md5[33];
......@@ -395,7 +396,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
{
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
thd->lex->unit.print(&str);
lex->unit.print(&str);
thd->variables.sql_mode|= sql_mode;
}
str.append('\0');
......@@ -474,21 +475,21 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->calc_md5(md5);
view->md5.str= md5;
view->md5.length= 32;
can_be_merged= thd->lex->can_be_merged();
if (thd->lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
!thd->lex->can_be_merged())
can_be_merged= lex->can_be_merged();
if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
!lex->can_be_merged())
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
ER(ER_WARN_VIEW_MERGE));
thd->lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
}
view->algorithm= thd->lex->create_view_algorithm;
view->with_check= thd->lex->create_view_check;
view->algorithm= lex->create_view_algorithm;
view->with_check= lex->create_view_check;
if ((view->updatable_view= (can_be_merged &&
view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
{
/* TODO: change here when we will support UNIONs */
for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first;
for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first;
tbl;
tbl= tbl->next_local)
{
......@@ -500,6 +501,26 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
}
}
/*
Check that table of main select do not used in subqueries.
This test can catch only very simple cases of such non-updateable views,
all other will be detected before updating commands execution.
(it is more optimisation then real check)
NOTE: this skip cases of using table via VIEWs, joined VIEWs, VIEWs with
UNION
*/
if (view->updatable_view &&
!lex->select_lex.next_select() &&
!((TABLE_LIST*)lex->select_lex.table_list.first)->next_local &&
find_table_in_global_list(lex->query_tables->next_global,
lex->query_tables->db,
lex->query_tables->real_name))
{
view->updatable_view= 0;
}
if (view->with_check != VIEW_CHECK_NONE &&
!view->updatable_view)
{
......@@ -698,13 +719,12 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
tables just after VIEW instead of tail of list, to be able check that
table is unique. Also we store old next table for the same purpose.
*/
table->old_next= table->next_global;
if (view_tables)
{
if (table->next_global)
{
view_tables_tail->next_global= table->next_global;
table->next_global->prev_global= &view_tables_tail->next_global;
view_tables_tail->next_global= table->old_next;
}
else
{
......
......@@ -232,8 +232,6 @@ typedef struct st_table_list
st_table_list *ancestor;
/* most upper view this table belongs to */
st_table_list *belong_to_view;
/* next_global before adding VIEW tables */
st_table_list *old_next;
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
LEX_STRING query; /* text of (CRETE/SELECT) statement */
......@@ -286,12 +284,6 @@ typedef struct st_table_list
bool setup_ancestor(THD *thd, Item **conds, uint8 check_option);
bool placeholder() {return derived || view; }
void print(THD *thd, String *str);
inline st_table_list *next_independent()
{
if (view)
return old_next;
return next_global;
}
} TABLE_LIST;
class Item;
......
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