diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index 3a70dcddd24..25fe18dccaf 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -317,3 +317,10 @@ SHOW COUNT(*) WARNINGS; SHOW COUNT(*) ERRORS; @@session.error_count 1 +create table t1(f1 int); +insert into t1 values(1),(1),(2); +select @a:=f1, count(f1) from t1 group by 1; +@a:=f1 count(f1) +1 2 +2 1 +drop table t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 9116695bbea..5ed06855f19 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3386,6 +3386,67 @@ WHERE t1.id=t2.id AND 1 IN (SELECT id FROM t1) WITH CHECK OPTION; UPDATE v1 SET c=1; DROP VIEW v1; DROP TABLE t1,t2; +CREATE TABLE t1 (a1 INT, c INT DEFAULT 0); +CREATE TABLE t2 (a2 INT); +CREATE TABLE t3 (a3 INT); +CREATE TABLE t4 (a4 INT); +INSERT INTO t1 (a1) VALUES (1),(2); +INSERT INTO t2 (a2) VALUES (1),(2); +INSERT INTO t3 (a3) VALUES (1),(2); +INSERT INTO t4 (a4) VALUES (1),(2); +CREATE VIEW v1 AS +SELECT t1.a1, t1.c FROM t1 JOIN t2 ON t1.a1=t2.a2 AND t1.c < 3 +WITH CHECK OPTION; +SELECT * FROM v1; +a1 c +1 0 +2 0 +UPDATE v1 SET c=3; +ERROR HY000: CHECK OPTION failed 'test.v1' +PREPARE t FROM 'UPDATE v1 SET c=3'; +EXECUTE t; +ERROR HY000: CHECK OPTION failed 'test.v1' +EXECUTE t; +ERROR HY000: CHECK OPTION failed 'test.v1' +INSERT INTO v1(a1, c) VALUES (3, 3); +ERROR HY000: CHECK OPTION failed 'test.v1' +UPDATE v1 SET c=1 WHERE a1=1; +SELECT * FROM v1; +a1 c +1 1 +2 0 +SELECT * FROM t1; +a1 c +1 1 +2 0 +CREATE VIEW v2 AS SELECT t1.a1, t1.c +FROM (t1 JOIN t2 ON t1.a1=t2.a2 AND t1.c < 3) +JOIN (t3 JOIN t4 ON t3.a3=t4.a4) +ON t2.a2=t3.a3 WITH CHECK OPTION; +SELECT * FROM v2; +a1 c +1 1 +2 0 +UPDATE v2 SET c=3; +ERROR HY000: CHECK OPTION failed 'test.v2' +PREPARE t FROM 'UPDATE v2 SET c=3'; +EXECUTE t; +ERROR HY000: CHECK OPTION failed 'test.v2' +EXECUTE t; +ERROR HY000: CHECK OPTION failed 'test.v2' +INSERT INTO v2(a1, c) VALUES (3, 3); +ERROR HY000: CHECK OPTION failed 'test.v2' +UPDATE v2 SET c=2 WHERE a1=1; +SELECT * FROM v2; +a1 c +1 2 +2 0 +SELECT * FROM t1; +a1 c +1 2 +2 0 +DROP VIEW v1,v2; +DROP TABLE t1,t2,t3,t4; End of 5.0 tests. DROP DATABASE IF EXISTS `d-1`; CREATE DATABASE `d-1`; diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 70f57fdf283..7919b663a73 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -222,3 +222,11 @@ drop table t1,t2; insert into city 'blah'; SHOW COUNT(*) WARNINGS; SHOW COUNT(*) ERRORS; + +# +# Bug#28494: Grouping by Item_func_set_user_var produces incorrect result. +# +create table t1(f1 int); +insert into t1 values(1),(1),(2); +select @a:=f1, count(f1) from t1 group by 1; +drop table t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index f16bb6efd7a..68817b0771a 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3260,6 +3260,58 @@ UPDATE v1 SET c=1; DROP VIEW v1; DROP TABLE t1,t2; +# +# Bug #27827: CHECK OPTION ignores ON conditions when updating +# a multi-table view with CHECK OPTION. +# + +CREATE TABLE t1 (a1 INT, c INT DEFAULT 0); +CREATE TABLE t2 (a2 INT); +CREATE TABLE t3 (a3 INT); +CREATE TABLE t4 (a4 INT); +INSERT INTO t1 (a1) VALUES (1),(2); +INSERT INTO t2 (a2) VALUES (1),(2); +INSERT INTO t3 (a3) VALUES (1),(2); +INSERT INTO t4 (a4) VALUES (1),(2); + +CREATE VIEW v1 AS + SELECT t1.a1, t1.c FROM t1 JOIN t2 ON t1.a1=t2.a2 AND t1.c < 3 + WITH CHECK OPTION; +SELECT * FROM v1; +--error 1369 +UPDATE v1 SET c=3; +PREPARE t FROM 'UPDATE v1 SET c=3'; +--error 1369 +EXECUTE t; +--error 1369 +EXECUTE t; +--error 1369 +INSERT INTO v1(a1, c) VALUES (3, 3); +UPDATE v1 SET c=1 WHERE a1=1; +SELECT * FROM v1; +SELECT * FROM t1; + +CREATE VIEW v2 AS SELECT t1.a1, t1.c + FROM (t1 JOIN t2 ON t1.a1=t2.a2 AND t1.c < 3) + JOIN (t3 JOIN t4 ON t3.a3=t4.a4) + ON t2.a2=t3.a3 WITH CHECK OPTION; +SELECT * FROM v2; +--error 1369 +UPDATE v2 SET c=3; +PREPARE t FROM 'UPDATE v2 SET c=3'; +--error 1369 +EXECUTE t; +--error 1369 +EXECUTE t; +--error 1369 +INSERT INTO v2(a1, c) VALUES (3, 3); +UPDATE v2 SET c=2 WHERE a1=1; +SELECT * FROM v2; +SELECT * FROM t1; + +DROP VIEW v1,v2; +DROP TABLE t1,t2,t3,t4; + --echo End of 5.0 tests. # diff --git a/sql/item_func.cc b/sql/item_func.cc index ad1096d0850..4f6c2774c82 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4259,9 +4259,11 @@ void Item_func_set_user_var::make_field(Send_field *tmp_field) TRUE Error */ -int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions) +int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions, + bool can_use_result_field) { - bool use_result_field= (result_field && result_field != field); + bool use_result_field= (!can_use_result_field ? 0 : + (result_field && result_field != field)); int error; /* Update the value of the user variable */ diff --git a/sql/item_func.h b/sql/item_func.h index b76877f90bc..b06ad073a32 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1248,7 +1248,13 @@ public: void print(String *str); void print_as_stmt(String *str); const char *func_name() const { return "set_user_var"; } - int save_in_field(Field *field, bool no_conversions); + int save_in_field(Field *field, bool no_conversions, + bool can_use_result_field); + int save_in_field(Field *field, bool no_conversions) + { + return save_in_field(field, no_conversions, 1); + } + void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); } }; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 46ff04702fd..07e8b06e0c8 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -706,7 +706,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, DBUG_PRINT("info", ("View: %s", str.ptr())); /* fill structure */ - view->query.str= str.c_ptr(); + view->query.str= str.c_ptr_safe(); view->query.length= str.length(); view->source.str= thd->query + thd->lex->create_view_select_start; endp= view->source.str; diff --git a/sql/table.cc b/sql/table.cc index 8eb01f06302..4e337552028 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2902,6 +2902,47 @@ bool st_table_list::prep_where(THD *thd, Item **conds, } +/* + Merge ON expressions for a view + + SYNOPSIS + merge_on_conds() + thd thread handle + table table for the VIEW + is_cascaded TRUE <=> merge ON expressions from underlying views + + DESCRIPTION + This function returns the result of ANDing the ON expressions + of the given view and all underlying views. The ON expressions + of the underlying views are added only if is_cascaded is TRUE. + + RETURN + Pointer to the built expression if there is any. + Otherwise and in the case of a failure NULL is returned. +*/ + +static Item * +merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) +{ + DBUG_ENTER("merge_on_conds"); + + Item *cond= NULL; + DBUG_PRINT("info", ("alias: %s", table->alias)); + if (table->on_expr) + cond= table->on_expr->copy_andor_structure(thd); + if (!table->nested_join) + DBUG_RETURN(cond); + List_iterator li(table->nested_join->join_list); + while (TABLE_LIST *tbl= li++) + { + if (tbl->view && !is_cascaded) + continue; + cond= and_conds(cond, merge_on_conds(thd, tbl, is_cascaded)); + } + DBUG_RETURN(cond); +} + + /* Prepare check option expression of table @@ -2918,8 +2959,8 @@ bool st_table_list::prep_where(THD *thd, Item **conds, VIEW_CHECK_LOCAL option. NOTE - This method build check options for every call - (usual execution or every SP/PS call) + This method builds check option condition to use it later on + every call (usual execution or every SP/PS call). This method have to be called after WHERE preparation (st_table_list::prep_where) @@ -2931,38 +2972,42 @@ bool st_table_list::prep_where(THD *thd, Item **conds, bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) { DBUG_ENTER("st_table_list::prep_check_option"); + bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED; for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { /* see comment of check_opt_type parameter */ - if (tbl->view && - tbl->prep_check_option(thd, - ((check_opt_type == VIEW_CHECK_CASCADED) ? - VIEW_CHECK_CASCADED : - VIEW_CHECK_NONE))) - { + if (tbl->view && tbl->prep_check_option(thd, (is_cascaded ? + VIEW_CHECK_CASCADED : + VIEW_CHECK_NONE))) DBUG_RETURN(TRUE); - } } - if (check_opt_type) + if (check_opt_type && !check_option_processed) { - Item *item= 0; + Query_arena *arena= thd->stmt_arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test + if (where) { DBUG_ASSERT(where->fixed); - item= where->copy_andor_structure(thd); + check_option= where->copy_andor_structure(thd); } - if (check_opt_type == VIEW_CHECK_CASCADED) + if (is_cascaded) { for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { if (tbl->check_option) - item= and_conds(item, tbl->check_option); + check_option= and_conds(check_option, tbl->check_option); } } - if (item) - thd->change_item_tree(&check_option, item); + check_option= and_conds(check_option, + merge_on_conds(thd, this, is_cascaded)); + + if (arena) + thd->restore_active_arena(arena, &backup); + check_option_processed= TRUE; + } if (check_option) @@ -3067,7 +3112,7 @@ void st_table_list::cleanup_items() check CHECK OPTION condition SYNOPSIS - check_option() + st_table_list::view_check_option() ignore_failure ignore check option fail RETURN diff --git a/sql/table.h b/sql/table.h index 99fbf57bb52..a87ca1de771 100644 --- a/sql/table.h +++ b/sql/table.h @@ -909,6 +909,8 @@ typedef struct st_table_list bool compact_view_format; /* Use compact format for SHOW CREATE VIEW */ /* view where processed */ bool where_processed; + /* TRUE <=> VIEW CHECK OPTION expression has been processed */ + bool check_option_processed; /* FRMTYPE_ERROR if any type is acceptable */ enum frm_type_enum required_type; handlerton *db_type; /* table_type for handler */