From 9f9893c97139098f27795a8cd96ef4b131a94a9e Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 25 Oct 2004 17:32:28 +0300 Subject: [PATCH] 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 --- mysql-test/r/view.result | 13 +++++++++++++ mysql-test/t/view.test | 18 +++++++++++++++++- sql/sql_acl.cc | 8 +++++++- sql/sql_base.cc | 20 ++++++++++++++------ sql/sql_delete.cc | 2 +- sql/sql_insert.cc | 2 +- sql/sql_parse.cc | 2 +- sql/sql_update.cc | 2 +- sql/sql_view.cc | 40 ++++++++++++++++++++++++++++++---------- sql/table.h | 8 -------- 10 files changed, 85 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index eb32a3eeb2b..18fa7c2e374 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -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; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index a63086acd71..27b127a0093 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -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; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 34aa0984798..e215c932599 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -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; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4f273fbd0c4..a453f91a70e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f9dba49d2e3..7163bf10bcc 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -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); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index d9002c2da29..7457858d192 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 079d8994549..d40a992337e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -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; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 00e70ccb484..d96f40365b0 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -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); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4cdbfe9728b..f745db3e3ff 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -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 { diff --git a/sql/table.h b/sql/table.h index 3e2a61d5a21..de048e7cc5c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -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;