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
This commit is contained in:
parent
4f410c4b69
commit
9f9893c971
@ -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 @@ unsent_create_error:
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user