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;
|
create view v1 as select * from t1;
|
||||||
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
|
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
|
||||||
drop database mysqltest;
|
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;
|
use mysqltest;
|
||||||
create view v1 as select * from t1;
|
create view v1 as select * from t1;
|
||||||
|
|
||||||
|
|
||||||
connection root;
|
connection root;
|
||||||
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
|
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
|
||||||
drop database mysqltest;
|
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,
|
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
|
||||||
const char *db, const char *table)
|
const char *db, const char *table)
|
||||||
{
|
{
|
||||||
|
/* --skip-grants */
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
grant->privilege= ~NO_ACCESS; // everything is allowed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* global privileges */
|
/* global privileges */
|
||||||
grant->privilege= thd->master_access;
|
grant->privilege= thd->master_access;
|
||||||
|
|
||||||
/* db privileges */
|
/* db privileges */
|
||||||
grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0);
|
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)
|
if (!grant_option)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -593,7 +593,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
|
|||||||
{
|
{
|
||||||
if ((!strcmp(table->db, db_name) &&
|
if ((!strcmp(table->db, db_name) &&
|
||||||
!strcmp(table->real_name, table_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_cache_key, db_name) &&
|
||||||
!strcmp(table->table->table_name, table_name)))
|
!strcmp(table->table->table_name, table_name)))
|
||||||
break;
|
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)
|
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;
|
TABLE_LIST *res;
|
||||||
const char *d_name= table->db, *t_name= table->real_name;
|
const char *d_name= table->db, *t_name= table->real_name;
|
||||||
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_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;
|
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.
|
if (!(res= find_table_in_global_list(table_list, d_name, t_name)) ||
|
||||||
return find_table_in_global_list(res->next_global, 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");
|
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
|
||||||
DBUG_RETURN(-1);
|
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);
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
|
||||||
DBUG_RETURN(-1);
|
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))))
|
setup_fields(thd, 0, table_list, update_values, 0, 0, 0))))
|
||||||
DBUG_RETURN(-1);
|
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);
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
|
@ -2810,7 +2810,7 @@ unsent_create_error:
|
|||||||
Is table which we are changing used somewhere in other parts of
|
Is table which we are changing used somewhere in other parts of
|
||||||
query
|
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 */
|
/* Using same table for INSERT and SELECT */
|
||||||
select_lex->options |= OPTION_BUFFER_RESULT;
|
select_lex->options |= OPTION_BUFFER_RESULT;
|
||||||
|
@ -529,7 +529,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
|
|||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
|
|
||||||
/* 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 */
|
||||||
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);
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
|
||||||
DBUG_RETURN(-1);
|
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,
|
static int mysql_register_view(THD *thd, TABLE_LIST *view,
|
||||||
enum_view_create_mode mode)
|
enum_view_create_mode mode)
|
||||||
{
|
{
|
||||||
|
LEX *lex= thd->lex;
|
||||||
char buff[4096];
|
char buff[4096];
|
||||||
String str(buff,(uint32) sizeof(buff), system_charset_info);
|
String str(buff,(uint32) sizeof(buff), system_charset_info);
|
||||||
char md5[33];
|
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;
|
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
|
||||||
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;
|
thd->variables.sql_mode|= sql_mode;
|
||||||
}
|
}
|
||||||
str.append('\0');
|
str.append('\0');
|
||||||
@ -474,21 +475,21 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
|
|||||||
view->calc_md5(md5);
|
view->calc_md5(md5);
|
||||||
view->md5.str= md5;
|
view->md5.str= md5;
|
||||||
view->md5.length= 32;
|
view->md5.length= 32;
|
||||||
can_be_merged= thd->lex->can_be_merged();
|
can_be_merged= lex->can_be_merged();
|
||||||
if (thd->lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
|
if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
|
||||||
!thd->lex->can_be_merged())
|
!lex->can_be_merged())
|
||||||
{
|
{
|
||||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
|
||||||
ER(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->algorithm= lex->create_view_algorithm;
|
||||||
view->with_check= thd->lex->create_view_check;
|
view->with_check= lex->create_view_check;
|
||||||
if ((view->updatable_view= (can_be_merged &&
|
if ((view->updatable_view= (can_be_merged &&
|
||||||
view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
|
view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
|
||||||
{
|
{
|
||||||
/* TODO: change here when we will support UNIONs */
|
/* 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= tbl->next_local)
|
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 &&
|
if (view->with_check != VIEW_CHECK_NONE &&
|
||||||
!view->updatable_view)
|
!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
|
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 is unique. Also we store old next table for the same purpose.
|
||||||
*/
|
*/
|
||||||
table->old_next= table->next_global;
|
|
||||||
if (view_tables)
|
if (view_tables)
|
||||||
{
|
{
|
||||||
if (table->next_global)
|
if (table->next_global)
|
||||||
{
|
{
|
||||||
|
view_tables_tail->next_global= table->next_global;
|
||||||
table->next_global->prev_global= &view_tables_tail->next_global;
|
table->next_global->prev_global= &view_tables_tail->next_global;
|
||||||
view_tables_tail->next_global= table->old_next;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -232,8 +232,6 @@ typedef struct st_table_list
|
|||||||
st_table_list *ancestor;
|
st_table_list *ancestor;
|
||||||
/* most upper view this table belongs to */
|
/* most upper view this table belongs to */
|
||||||
st_table_list *belong_to_view;
|
st_table_list *belong_to_view;
|
||||||
/* next_global before adding VIEW tables */
|
|
||||||
st_table_list *old_next;
|
|
||||||
Item *where; /* VIEW WHERE clause condition */
|
Item *where; /* VIEW WHERE clause condition */
|
||||||
Item *check_option; /* WITH CHECK OPTION condition */
|
Item *check_option; /* WITH CHECK OPTION condition */
|
||||||
LEX_STRING query; /* text of (CRETE/SELECT) statement */
|
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 setup_ancestor(THD *thd, Item **conds, uint8 check_option);
|
||||||
bool placeholder() {return derived || view; }
|
bool placeholder() {return derived || view; }
|
||||||
void print(THD *thd, String *str);
|
void print(THD *thd, String *str);
|
||||||
inline st_table_list *next_independent()
|
|
||||||
{
|
|
||||||
if (view)
|
|
||||||
return old_next;
|
|
||||||
return next_global;
|
|
||||||
}
|
|
||||||
} TABLE_LIST;
|
} TABLE_LIST;
|
||||||
|
|
||||||
class Item;
|
class Item;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user