diff --git a/client/mysql.cc b/client/mysql.cc index 82684b390cd96..67435704fcbdd 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -914,6 +914,7 @@ static COMMANDS commands[] = { { "ADDTIME", 0, 0, 0, ""}, { "AES_ENCRYPT", 0, 0, 0, ""}, { "AES_DECRYPT", 0, 0, 0, ""}, + { "ANY_VALUE", 0, 0, 0, ""}, { "AREA", 0, 0, 0, ""}, { "ASIN", 0, 0, 0, ""}, { "ASBINARY", 0, 0, 0, ""}, diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result new file mode 100644 index 0000000000000..b1e8a84b22da2 --- /dev/null +++ b/mysql-test/main/any_value.result @@ -0,0 +1,96 @@ +CREATE TABLE t0 (a INT, b INT); +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (c INT, d INT); +CREATE TABLE t3 (x INT, y INT, z INT); +CREATE TABLE t4 (x INT, y INT, INDEX idx (y, x)); +CREATE TABLE t5 (x INT, y CHAR(1), z INT); +INSERT INTO t1 VALUES (1,10),(2,10),(NULL,20),(3,20),(NULL,30),(4,40); +INSERT INTO t2 VALUES (1,100),(2,200),(3,300); +INSERT INTO t3 VALUES (1,10,10),(2,10,10),(3,20,20),(4,20,20),(5,30,30); +INSERT INTO t4 VALUES (1,10),(2,10),(3,20),(4,20),(5,30); +INSERT INTO t5 VALUES (1,'a',1),(1,'a',2),(1,'b',3),(1,'b',4),(2,'a',5),(2,'a',6),(2,'b',7); +SET SQL_MODE=ONLY_FULL_GROUP_BY; +SELECT ANY_VALUE(b) FROM t0; +ANY_VALUE(b) +NULL +SELECT ANY_VALUE(b) FROM t1; +ANY_VALUE(b) +10 +SELECT COUNT(*), ANY_VALUE(b) FROM t1; +COUNT(*) ANY_VALUE(b) +6 10 +SELECT c FROM t2 WHERE ANY_VALUE(d) = 200; +ERROR HY000: Invalid use of group function +SELECT ANY_VALUE(b) FROM t0 GROUP BY b; +ANY_VALUE(b) +SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; +ANY_VALUE(a) b +1 10 +NULL 20 +NULL 30 +4 40 +SELECT ANY_VALUE(b) FROM t1 GROUP BY b; +ANY_VALUE(b) +10 +20 +30 +40 +SELECT b, ANY_VALUE(a + 100) FROM t1 GROUP BY b; +b ANY_VALUE(a + 100) +10 101 +20 NULL +30 NULL +40 104 +SELECT b, ANY_VALUE(a * 2 + b) FROM t1 GROUP BY b; +b ANY_VALUE(a * 2 + b) +10 12 +20 NULL +30 NULL +40 48 +SELECT y, ANY_VALUE(x + z) FROM t3 GROUP BY y; +y ANY_VALUE(x + z) +10 11 +20 23 +30 35 +SELECT y, ANY_VALUE(x) FROM t4 GROUP BY y; +y ANY_VALUE(x) +10 1 +20 3 +30 5 +EXPLAIN SELECT y, ANY_VALUE(x) FROM t4 GROUP BY y; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t4 index NULL idx 10 NULL 5 Using index +SELECT b, (SELECT SUM(t2.d) + SUM(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; +b sub +10 603 +20 603 +30 NULL +40 604 +SELECT b, (SELECT SUM(t2.d) + ANY_VALUE(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; +b sub +10 601 +20 NULL +30 NULL +40 604 +SELECT y, ANY_VALUE(z) FROM t3 GROUP BY y HAVING ANY_VALUE(z) = 20; +y ANY_VALUE(z) +20 20 +SELECT x, y, ANY_VALUE(z) FROM t5 GROUP BY x, y WITH ROLLUP; +x y ANY_VALUE(z) +1 a 1 +1 b 3 +1 NULL 1 +2 a 5 +2 b 7 +2 NULL 5 +NULL NULL 1 +DROP TABLE t0,t1,t2,t3,t4,t5; +CREATE TABLE t (a CHAR CHARACTER SET latin2); +INSERT INTO t VALUES ('a'),('b'); +SELECT charset(ANY_VALUE(a)), collation(ANY_VALUE(a)) FROM t; +charset(ANY_VALUE(a)) collation(ANY_VALUE(a)) +latin2 latin2_general_ci +SELECT coercibility(ANY_VALUE(a)), coercibility(ANY_VALUE('x')) FROM t; +coercibility(ANY_VALUE(a)) coercibility(ANY_VALUE('x')) +2 6 +DROP TABLE t; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test new file mode 100644 index 0000000000000..a7137d03516c7 --- /dev/null +++ b/mysql-test/main/any_value.test @@ -0,0 +1,73 @@ +CREATE TABLE t0 (a INT, b INT); +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (c INT, d INT); +CREATE TABLE t3 (x INT, y INT, z INT); +CREATE TABLE t4 (x INT, y INT, INDEX idx (y, x)); +CREATE TABLE t5 (x INT, y CHAR(1), z INT); +INSERT INTO t1 VALUES (1,10),(2,10),(NULL,20),(3,20),(NULL,30),(4,40); +INSERT INTO t2 VALUES (1,100),(2,200),(3,300); +INSERT INTO t3 VALUES (1,10,10),(2,10,10),(3,20,20),(4,20,20),(5,30,30); +INSERT INTO t4 VALUES (1,10),(2,10),(3,20),(4,20),(5,30); +INSERT INTO t5 VALUES (1,'a',1),(1,'a',2),(1,'b',3),(1,'b',4),(2,'a',5),(2,'a',6),(2,'b',7); + +SET SQL_MODE=ONLY_FULL_GROUP_BY; + +# +# Without GROUP BY +# + +SELECT ANY_VALUE(b) FROM t0; +SELECT ANY_VALUE(b) FROM t1; + +# Mixing real aggregate function +SELECT COUNT(*), ANY_VALUE(b) FROM t1; + +# In WHERE +--error ER_INVALID_GROUP_FUNC_USE +SELECT c FROM t2 WHERE ANY_VALUE(d) = 200; + +# +# With GROUP BY +# + +SELECT ANY_VALUE(b) FROM t0 GROUP BY b; + +# Regular use in SELECT-list +SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; +SELECT ANY_VALUE(b) FROM t1 GROUP BY b; + +# Expression inside ANY_VALUE +SELECT b, ANY_VALUE(a + 100) FROM t1 GROUP BY b; +SELECT b, ANY_VALUE(a * 2 + b) FROM t1 GROUP BY b; +SELECT y, ANY_VALUE(x + z) FROM t3 GROUP BY y; + +# Table with index on GROUP BY column +SELECT y, ANY_VALUE(x) FROM t4 GROUP BY y; +EXPLAIN SELECT y, ANY_VALUE(x) FROM t4 GROUP BY y; + +# Correlated subquery +SELECT b, (SELECT SUM(t2.d) + SUM(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; + +SELECT b, (SELECT SUM(t2.d) + ANY_VALUE(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; + +# In HAVING +SELECT y, ANY_VALUE(z) FROM t3 GROUP BY y HAVING ANY_VALUE(z) = 20; + +# Rollup +SELECT x, y, ANY_VALUE(z) FROM t5 GROUP BY x, y WITH ROLLUP; + +DROP TABLE t0,t1,t2,t3,t4,t5; + +# +# Charset/Collation/Coercibility Tests +# + +CREATE TABLE t (a CHAR CHARACTER SET latin2); +INSERT INTO t VALUES ('a'),('b'); + +# Charset/Collation preserved + +SELECT charset(ANY_VALUE(a)), collation(ANY_VALUE(a)) FROM t; +SELECT coercibility(ANY_VALUE(a)), coercibility(ANY_VALUE('x')) FROM t; + +DROP TABLE t; diff --git a/mysql-test/main/group_by.result b/mysql-test/main/group_by.result index 57a3cf97e94eb..1034abeb3c616 100644 --- a/mysql-test/main/group_by.result +++ b/mysql-test/main/group_by.result @@ -1047,7 +1047,10 @@ m 3 SELECT (SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) FROM t1 AS t1_outer GROUP BY t1_outer.b; -ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +(SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) +3 +7 +11 SELECT 1 FROM t1 as t1_outer WHERE (SELECT t1_outer.b FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1); 1 @@ -3154,3 +3157,48 @@ we BPYFY BPYFY we c c DROP TABLE t1, t2; # End of 11.8 tests +# +# MDEV-39932: Assert correct ONLY_FULL_GROUP_BY behavior with outer refs in correlated subqueries +# +SET @save_sql_mode=@@sql_mode; +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1),(2,1),(3,2),(4,2),(5,3),(6,3); +SELECT (SELECT SUM(t1_inner.a) + t1_outer.a FROM t1 AS t1_inner LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT (SELECT SUM(t1_outer.b) + t1_outer.a FROM t1 AS t1_inner LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT (SELECT SUM(t1_inner.a + t1_outer.a) FROM t1 AS t1_inner LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT (SELECT SUM(t1_outer.a) + t1_outer.b FROM t1 AS t1_inner LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +c1 +4 +9 +14 +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner WHERE t1_inner.a = t1_outer.a LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner WHERE t1_outer.a > 5 LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner WHERE SUM(t1_outer.a) > 5 LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +c1 +NULL +1 +1 +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner GROUP BY t1_inner.a HAVING t1_inner.a + SUM(t1_outer.a) > 5 LIMIT 1) AS c1 +FROM t1 AS t1_outer GROUP BY t1_outer.b; +c1 +3 +1 +1 +DROP TABLE t1; +SET sql_mode=@save_sql_mode; +# +# End of 10.11 tests +# diff --git a/mysql-test/main/group_by.test b/mysql-test/main/group_by.test index 8009d976d191e..2c5d623331802 100644 --- a/mysql-test/main/group_by.test +++ b/mysql-test/main/group_by.test @@ -793,7 +793,6 @@ SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1) AS m FROM t1 AS t1_outer; ---error ER_WRONG_FIELD_WITH_GROUP SELECT (SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) FROM t1 AS t1_outer GROUP BY t1_outer.b; @@ -2231,3 +2230,48 @@ DROP TABLE t1, t2; --echo # End of 11.8 tests + +--echo # +--echo # MDEV-39932: Assert correct ONLY_FULL_GROUP_BY behavior with outer refs in correlated subqueries +--echo # +SET @save_sql_mode=@@sql_mode; +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1),(2,1),(3,2),(4,2),(5,3),(6,3); + +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT SUM(t1_inner.a) + t1_outer.a FROM t1 AS t1_inner LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT SUM(t1_outer.b) + t1_outer.a FROM t1 AS t1_inner LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT SUM(t1_inner.a + t1_outer.a) FROM t1 AS t1_inner LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +SELECT (SELECT SUM(t1_outer.a) + t1_outer.b FROM t1 AS t1_inner LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner WHERE t1_inner.a = t1_outer.a LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner WHERE t1_outer.a > 5 LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner WHERE SUM(t1_outer.a) > 5 LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +SELECT (SELECT t1_inner.a FROM t1 AS t1_inner GROUP BY t1_inner.a HAVING t1_inner.a + SUM(t1_outer.a) > 5 LIMIT 1) AS c1 + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +DROP TABLE t1; +SET sql_mode=@save_sql_mode; + +--echo # +--echo # End of 10.11 tests +--echo # diff --git a/sql/item.cc b/sql/item.cc index e7cca88d9e7d9..e49d15005ec9a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6155,6 +6155,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (*from_field) { if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS) { /* diff --git a/sql/item_sum.cc b/sql/item_sum.cc index efc5b9450c3f6..d319a048c4c4c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -324,15 +324,20 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) in_sum_func->outer_fields.push_back(field, thd->mem_root); } else + { sel->set_non_agg_field_used(true); + sel->join->non_agg_fields.push_back(field, thd->mem_root); + } } - if (sel->nest_level > aggr_level && - (sel->agg_func_used()) && - !sel->group_list.elements) + else if (sel->nest_level > aggr_level) { - my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, - ER_THD(thd, ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); - return TRUE; + if ((sel->agg_func_used()) && !sel->group_list.elements) + { + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER_THD(thd, ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + return TRUE; + } + sel->join->non_agg_fields.push_back(field, thd->mem_root); } } } @@ -2610,6 +2615,46 @@ bool Item_sum_max::add() } +bool Item_sum_any_value::add() +{ + DBUG_ENTER("Item_sum_any_value::add"); + + if (!has_value) + { + value->store(args[0]); + value->cache_value(); + null_value= args[0]->null_value; + has_value= 1; + } + DBUG_RETURN(0); +} + + +void Item_sum_any_value::clear() +{ + DBUG_ENTER("Item_sum_any_value::clear"); + if (!const_item()) + { + value->clear(); + null_value= 1; + has_value= 0; + } + DBUG_VOID_RETURN; +} + + +void Item_sum_any_value::update_field() {} + + +Item *Item_sum_any_value::copy_or_same(THD* thd) +{ + DBUG_ENTER("Item_sum_any_value::copy_or_same"); + Item_sum_any_value *item= new (thd->mem_root) Item_sum_any_value(thd, this); + item->setup_hybrid(thd, args[0], value); + DBUG_RETURN(item); +} + + /* bit_or and bit_and */ longlong Item_sum_bit::val_int() diff --git a/sql/item_sum.h b/sql/item_sum.h index 39ed79e7c0203..14c98fc8c8e4d 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -348,7 +348,7 @@ class Item_sum :public Item_func_or_sum enum Sumfunctype { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, - AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, + AVG_DISTINCT_FUNC, ANY_VALUE_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC, ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC, CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC, @@ -421,6 +421,7 @@ class Item_sum :public Item_func_or_sum bool is_aggr_sum_func() { switch (sum_func()) { + case ANY_VALUE_FUNC: case COUNT_FUNC: case COUNT_DISTINCT_FUNC: case SUM_FUNC: @@ -1255,6 +1256,41 @@ class Item_sum_max final :public Item_sum_min_max }; +class Item_sum_any_value final : public Item_sum_min_max +{ +public: + Item_sum_any_value(THD *thd, Item *item_par) + : Item_sum_min_max(thd, item_par, 1), has_value(FALSE) + { + } + Item_sum_any_value(THD *thd, Item_sum_any_value *item) + : Item_sum_min_max(thd, item), has_value(item->has_value) + { + } + + bool add() override; + void clear() override; + void update_field() override; + + enum Sumfunctype sum_func() const override { return ANY_VALUE_FUNC; } + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING sum_name= {STRING_WITH_LEN("any_value(")}; + return sum_name; + } + Item *copy_or_same(THD* thd) override; + +protected: + Item *shallow_copy(THD *thd) const override + { + return get_item_copy(thd, this); + } + +private: + bool has_value; +}; + + class Item_sum_bit :public Item_sum_int { public: diff --git a/sql/lex.h b/sql/lex.h index 079633536c0fc..f156b87a59a6e 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -70,6 +70,7 @@ SYMBOL symbols[] = { { "ANALYZE", SYM(ANALYZE_SYM)}, { "AND", SYM(AND_SYM)}, { "ANY", SYM(ANY_SYM)}, + { "ANY_VALUE", SYM(ANY_VALUE_SYM)}, { "ARRAY", SYM(ARRAY_SYM)}, { "AS", SYM(AS)}, { "ASC", SYM(ASC)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index df4395395dcde..ff28ef8d12f9e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -483,6 +483,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token ALTER /* SQL-2003-R */ %token ANALYZE_SYM %token AND_SYM /* SQL-2003-R */ +%token ANY_VALUE_SYM %token ASC /* SQL-2003-N */ %token ASENSITIVE_SYM /* FUTURE-USE */ %token AS /* SQL-2003-R */ @@ -11540,7 +11541,19 @@ udf_expr: ; sum_expr: - AVG_SYM '(' in_sum_expr ')' + ANY_VALUE_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_any_value(thd, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + | ANY_VALUE_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_any_value(thd, $4); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + | AVG_SYM '(' in_sum_expr ')' { $$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE); if (unlikely($$ == NULL))