diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 3f12a582868..18fa7c2e374 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1626,3 +1626,23 @@ Field 3,'Field 4| |Field 6| | 'Field 7'| drop view v1; drop table t1; +create database mysqltest; +create table mysqltest.t1 (a int); +grant all privileges on mysqltest.* to mysqltest_1@localhost; +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 8e38b5616f8..27b127a0093 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1554,3 +1554,37 @@ select concat('|',a,'|'), concat('|',b,'|') from v1; drop view v1; drop table t1; +# +# user with global DB privileges +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings +create table mysqltest.t1 (a int); +grant all privileges on mysqltest.* to mysqltest_1@localhost; + +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 bed3130462d..a766081f0a0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3936,15 +3936,22 @@ 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; - /* if privileges ignored (--skip-grant-tables) above is enough */ + /* db privileges */ + grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0); + if (!grant_option) return; - /* db privileges */ - grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0); /* table privileges */ if (grant->version != grant_version) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 959af0067e7..c6e11bc73cc 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -595,7 +595,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; @@ -620,6 +621,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]; @@ -646,13 +649,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 37121e64ecc..8bdf19195f3 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 63e11822f6e..b0115dcdb17 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 5c83a746f73..3ac6d8ccbd7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2840,7 +2840,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 7bd445088cf..561480bd289 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 f8994148ffc..b37759cc256 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -383,6 +383,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]; @@ -396,7 +397,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'); @@ -475,21 +476,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) { @@ -501,6 +502,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) { @@ -699,13 +720,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 af7d90a8291..f95be1fcccb 100644 --- a/sql/table.h +++ b/sql/table.h @@ -260,8 +260,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 */ @@ -315,12 +313,6 @@ typedef struct st_table_list void cleanup_items(); 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;