From d6e1377ac206718abd039426ab72690a9ee0b48e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 Feb 2012 10:28:23 +0200 Subject: [PATCH] Fix check of view updatability in case of underlying view changes its updatability. For single table update/insert added deep check of single tables (single_table_updatable()). For multi-table view insert added additional check of target table (check_view_single_update). Multi-update was correct. Test suite for all cases added. --- mysql-test/r/view.result | 27 ++++++++++++++++++++++++++- mysql-test/t/view.test | 22 +++++++++++++++++++++- sql/sql_insert.cc | 17 +++++++++++++---- sql/sql_update.cc | 3 ++- sql/table.cc | 22 ++++++++++++++++++++++ sql/table.h | 3 +++ 6 files changed, 87 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 22fd4eb1722..65d6574ac8b 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3930,10 +3930,35 @@ drop table t1,t2; # Bug #794005: crash in st_table::mark_virtual_columns_for_write # CREATE TABLE t1 (a int); +insert into t1 values (1); CREATE TABLE t2 (a int); +insert into t2 values (1); CREATE VIEW v2 AS SELECT * FROM t2; CREATE VIEW v1 AS SELECT * FROM v2; +CREATE VIEW v3 AS SELECT t2.a,v1.a as b FROM t2,v1 where t2.a=v1.a; CREATE OR REPLACE ALGORITHM = TEMPTABLE VIEW v2 AS SELECT * FROM t1; UPDATE v1 SET a = 10; -DROP VIEW v1,v2; +ERROR HY000: The target table v1 of the UPDATE is not updatable +REPLACE v1 SET a = 10; +ERROR HY000: The target table v1 of the INSERT is not insertable-into +INSERT into v1 values (20); +ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v3 SET b= 10; +ERROR HY000: The target table v2 of the UPDATE is not updatable +REPLACE v3 SET b= 10; +ERROR HY000: The target table v3 of the INSERT is not insertable-into +INSERT into v3(b) values (20); +ERROR HY000: The target table v3 of the INSERT is not insertable-into +UPDATE v3 SET a = 10; +REPLACE v3 SET a = 11; +INSERT INTO v3(a) values (20); +select * from t1; +a +1 +select * from t2; +a +10 +11 +20 +DROP VIEW v1,v2,v3; DROP TABLE t1,t2; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 462118af4ea..4be8aa89358 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3982,13 +3982,33 @@ drop table t1,t2; --echo # CREATE TABLE t1 (a int); +insert into t1 values (1); CREATE TABLE t2 (a int); +insert into t2 values (1); CREATE VIEW v2 AS SELECT * FROM t2; CREATE VIEW v1 AS SELECT * FROM v2; +CREATE VIEW v3 AS SELECT t2.a,v1.a as b FROM t2,v1 where t2.a=v1.a; CREATE OR REPLACE ALGORITHM = TEMPTABLE VIEW v2 AS SELECT * FROM t1; +--error ER_NON_UPDATABLE_TABLE UPDATE v1 SET a = 10; +--error ER_NON_INSERTABLE_TABLE +REPLACE v1 SET a = 10; +--error ER_NON_INSERTABLE_TABLE +INSERT into v1 values (20); +--error ER_NON_UPDATABLE_TABLE +UPDATE v3 SET b= 10; +--error ER_NON_INSERTABLE_TABLE +REPLACE v3 SET b= 10; +--error ER_NON_INSERTABLE_TABLE +INSERT into v3(b) values (20); +UPDATE v3 SET a = 10; +REPLACE v3 SET a = 11; +INSERT INTO v3(a) values (20); -DROP VIEW v1,v2; +select * from t1; +select * from t2; + +DROP VIEW v1,v2,v3; DROP TABLE t1,t2; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 99bebe827a4..c350d2deeee 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -103,7 +103,8 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); */ bool check_view_single_update(List &fields, List *values, - TABLE_LIST *view, table_map *map) + TABLE_LIST *view, table_map *map, + bool insert) { /* it is join view => we need to find the table for update */ List_iterator_fast it(fields); @@ -135,6 +136,14 @@ bool check_view_single_update(List &fields, List *values, goto error; view->table= tbl->table; + if (!tbl->single_table_updatable()) + { + if (insert) + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT"); + else + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "UPDATE"); + return TRUE; + } *map= tables; return FALSE; @@ -179,7 +188,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, { TABLE *table= table_list->table; - if (!table_list->updatable) + if (!table_list->single_table_updatable()) { my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); return -1; @@ -251,7 +260,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (check_view_single_update(fields, fields_and_values_from_different_maps ? (List*) 0 : &values, - table_list, map)) + table_list, map, true)) return -1; table= table_list->table; } @@ -337,7 +346,7 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && check_view_single_update(update_fields, &update_values, - insert_table_list, map)) + insert_table_list, map, false)) return -1; if (table->timestamp_field) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index c6f413e754e..b3c001849b5 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -277,7 +277,8 @@ int mysql_update(THD *thd, { DBUG_RETURN(1); } - if (!table_list->updatable || check_key_in_view(thd, table_list)) + if (!table_list->single_table_updatable() || + check_key_in_view(thd, table_list)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); diff --git a/sql/table.cc b/sql/table.cc index a3df9023805..e576e5ae25f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3722,6 +3722,28 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, DBUG_RETURN(FALSE); } +/** + Check that table/view is updatable and if it has single + underlying tables/views it is also updatable + + @return Result of the check. +*/ + +bool TABLE_LIST::single_table_updatable() +{ + if (!updatable) + return false; + if (view_tables && view_tables->elements == 1) + { + /* + We need to check deeply only single table views. Multi-table views + will be turned to multi-table updates and then checked by leaf tables + */ + return view_tables->head()->single_table_updatable(); + } + return true; +} + /* Merge ON expressions for a view diff --git a/sql/table.h b/sql/table.h index 1b7713022ce..fed30722d71 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1606,6 +1606,9 @@ struct TABLE_LIST */ char *get_table_name() { return view != NULL ? view_name.str : table_name; } + + bool single_table_updatable(); + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause);