From e7d3927acc88a5ca18e9097d946377e2d1f290dc Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sun, 31 May 2026 12:14:01 -0400 Subject: [PATCH 01/14] Create Item_func_any_value and Create_func_any_value --- client/mysql.cc | 1 + sql/item_cmpfunc.h | 19 +++++++++++++++++++ sql/item_create.cc | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+) 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/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 22a8cebca668f..18374e2e69887 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1250,6 +1250,8 @@ class Item_func_interval :public Item_long_func class Item_func_coalesce :public Item_func_case_expression { public: + Item_func_coalesce(THD *thd, Item *a): + Item_func_case_expression(thd, a) {} Item_func_coalesce(THD *thd, Item *a, Item *b): Item_func_case_expression(thd, a, b) {} Item_func_coalesce(THD *thd, List &list): @@ -1289,6 +1291,23 @@ class Item_func_coalesce :public Item_func_case_expression }; +class Item_func_any_value final :public Item_func_coalesce +{ +public: + Item_func_any_value(THD *thd, Item *a): + Item_func_coalesce(thd, a) {} + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("any_value") }; + return name; + } + +protected: + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; + + /* Case abbreviations that aggregate its result field type by two arguments: IFNULL(arg1, arg2) diff --git a/sql/item_create.cc b/sql/item_create.cc index f2716e643668a..e2df1f626795f 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -168,6 +168,19 @@ class Create_func_aes_decrypt : public Create_native_func }; +class Create_func_any_value : public Create_func_arg1 +{ +public: + Item *create_1_arg(THD *thd, Item *arg1) override; + + static Create_func_any_value s_singleton; + +protected: + Create_func_any_value() = default; + ~Create_func_any_value() override = default; +}; + + class Create_func_kdf : public Create_native_func { public: @@ -3252,6 +3265,15 @@ Create_func_aes_decrypt::create_native(THD *thd, const LEX_CSTRING *name, } +Create_func_any_value Create_func_any_value::s_singleton; + +Item* +Create_func_any_value::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_any_value(thd, arg1); +} + + Create_func_kdf Create_func_kdf::s_singleton; Item* @@ -6330,6 +6352,7 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("ADD_MONTHS") }, BUILDER(Create_func_addmonths)}, { { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)}, { { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)}, + { { STRING_WITH_LEN("ANY_VALUE") }, BUILDER(Create_func_any_value)}, { { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)}, { { STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, { { STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, From ed315b57733bc8687975fb288bb4aa23b9ae1067 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Fri, 5 Jun 2026 17:32:05 -0400 Subject: [PATCH 02/14] Track ANY_VALUE nesting context in fix_fields --- sql/item_cmpfunc.cc | 14 ++++++++++++++ sql/item_cmpfunc.h | 3 +++ sql/sql_lex.h | 1 + 3 files changed, 18 insertions(+) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index eaa705642d843..a48911c4ce1a9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -3724,6 +3724,20 @@ Type_ref_null Item_func_coalesce::ref_op(THD *thd) } +bool Item_func_any_value::fix_fields(THD *thd, Item **ref) +{ + in_any_value= thd->lex->in_any_value; + thd->lex->in_any_value= this; + nest_level= thd->lex->current_select->nest_level; + + if (Item_func::fix_fields(thd, ref)) + return 1; + + thd->lex->in_any_value= in_any_value; + + return 0; +} + /**************************************************************************** Classes and function for the IN operator diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 18374e2e69887..32f9078d8ee55 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1294,6 +1294,8 @@ class Item_func_coalesce :public Item_func_case_expression class Item_func_any_value final :public Item_func_coalesce { public: + int8 nest_level; + Item_func_any_value *in_any_value; Item_func_any_value(THD *thd, Item *a): Item_func_coalesce(thd, a) {} LEX_CSTRING func_name_cstring() const override @@ -1301,6 +1303,7 @@ class Item_func_any_value final :public Item_func_coalesce static LEX_CSTRING name= {STRING_WITH_LEN("any_value") }; return name; } + bool fix_fields(THD *thd, Item **ref) override; protected: Item *shallow_copy(THD *thd) const override diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2e61fc1feb550..f9107c12b8682 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3387,6 +3387,7 @@ struct LEX: public Query_tables_list Column_definition *last_field; Table_function_json_table *json_table; Item_sum *in_sum_func; + Item_func_any_value *in_any_value; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options Table_specification_st create_info; From c009a6deff2c856a83f3a4e4774c6470ef4b9d09 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Fri, 5 Jun 2026 21:47:58 -0400 Subject: [PATCH 03/14] Allow in_any_value to bypass set_non_agg_field_used in fix_fields --- sql/item.cc | 5 +++-- sql/sql_lex.cc | 1 + sql/sql_parse.cc | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index e7cca88d9e7d9..bf7b5bec6d596 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6733,9 +6733,10 @@ bool Item_field::fix_fields(THD *thd, Item **reference) */ select_lex= context->select_lex; } - if (!thd->lex->in_sum_func) + if (!thd->lex->in_sum_func && !thd->lex->in_any_value) select_lex->set_non_agg_field_used(true); - else + else if (!thd->lex->in_any_value || + thd->lex->in_any_value->nest_level != select->nest_level) { if (outer_fixed) thd->lex->in_sum_func->outer_fields.push_back(this, thd->mem_root); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index beda9a6b03fc3..6188814f328a5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1315,6 +1315,7 @@ void LEX::start(THD *thd_arg) builtin_select.nest_level_base= &unit; allow_sum_func.clear_all(); in_sum_func= NULL; + in_any_value= NULL; used_tables= 0; table_type= TABLE_TYPE_UNKNOWN; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bcd6de564f550..a504674c42cb2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3072,6 +3072,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp) thd->variables.select_limit= HA_POS_ERROR; thd->lex->in_sum_func= 0; // For Item_field::fix_fields() + thd->lex->in_any_value= 0; /* We never write CALL statements into binlog: From 28b94511cd74b2e85e776d6a4bae68f790f94d20 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sun, 7 Jun 2026 22:52:44 -0400 Subject: [PATCH 04/14] Modify Item_field::fix_fields and Item_field::fix_outer_field to push to non_agg_fields only if BOTH in_sum_func and in_any_value are false --- mysql-test/main/any_value.result | 22 ++++++++++++++++++++++ mysql-test/main/any_value.test | 16 ++++++++++++++++ sql/item.cc | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 mysql-test/main/any_value.result create mode 100644 mysql-test/main/any_value.test diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result new file mode 100644 index 0000000000000..b6e253ddf9d81 --- /dev/null +++ b/mysql-test/main/any_value.result @@ -0,0 +1,22 @@ +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (c INT, d INT); +INSERT INTO t1 VALUES (1,10),(2,10),(3,20); +INSERT INTO t2 VALUES (1,100),(2,200),(3,300); +SET SQL_MODE=ONLY_FULL_GROUP_BY; +SELECT COUNT(*), ANY_VALUE(b) FROM t1; +COUNT(*) ANY_VALUE(b) +3 10 +SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; +ANY_VALUE(a) b +1 10 +3 20 +SELECT b, (SELECT SUM(t2.d) + SUM(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; +b sub +10 603 +20 603 +SELECT b, (SELECT SUM(t2.d) + ANY_VALUE(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; +b sub +10 601 +20 603 +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test new file mode 100644 index 0000000000000..28f6b688b3600 --- /dev/null +++ b/mysql-test/main/any_value.test @@ -0,0 +1,16 @@ +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (c INT, d INT); +INSERT INTO t1 VALUES (1,10),(2,10),(3,20); +INSERT INTO t2 VALUES (1,100),(2,200),(3,300); + +SET SQL_MODE=ONLY_FULL_GROUP_BY; + +SELECT COUNT(*), ANY_VALUE(b) FROM t1; +SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; + +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; + +DROP TABLE t1; +DROP TABLE t2; \ No newline at end of file diff --git a/sql/item.cc b/sql/item.cc index bf7b5bec6d596..607a87ea61126 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 && !thd->lex->in_any_value && select->cur_pos_in_select_list != UNDEF_POS) { /* @@ -6697,7 +6698,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (check_ora_join(reference, outer_fixed)) goto error; if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && - !outer_fixed && !thd->lex->in_sum_func && + !outer_fixed && !thd->lex->in_sum_func && !thd->lex->in_any_value && select && select->cur_pos_in_select_list != UNDEF_POS && select->join) From 579b2f0ca6a051279e93f246be661d493d82f1b4 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Mon, 8 Jun 2026 18:47:39 -0400 Subject: [PATCH 05/14] Modify existing group_by test to allow referencing outer field in correlated subquery as long as it is aggregated --- mysql-test/main/any_value.test | 2 +- mysql-test/main/group_by.result | 5 ++++- mysql-test/main/group_by.test | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test index 28f6b688b3600..8f5fc65274bdd 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -13,4 +13,4 @@ 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; DROP TABLE t1; -DROP TABLE t2; \ No newline at end of file +DROP TABLE t2; diff --git a/mysql-test/main/group_by.result b/mysql-test/main/group_by.result index 57a3cf97e94eb..f4c1035622d8d 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 diff --git a/mysql-test/main/group_by.test b/mysql-test/main/group_by.test index 8009d976d191e..92594f37d2831 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; From a678ee32b6f7eca297fcf73fc5824d1b1b5a9308 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Mon, 8 Jun 2026 19:45:50 -0400 Subject: [PATCH 06/14] Add additional test cases to any_value --- mysql-test/main/any_value.result | 20 ++++++++++++++++++++ mysql-test/main/any_value.test | 13 +++++++++++++ 2 files changed, 33 insertions(+) diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result index b6e253ddf9d81..3a29f63d25c95 100644 --- a/mysql-test/main/any_value.result +++ b/mysql-test/main/any_value.result @@ -1,7 +1,9 @@ CREATE TABLE t1 (a INT, b INT); CREATE TABLE t2 (c INT, d INT); +CREATE TABLE t3 (x INT, y INT, z INT); INSERT INTO t1 VALUES (1,10),(2,10),(3,20); 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); SET SQL_MODE=ONLY_FULL_GROUP_BY; SELECT COUNT(*), ANY_VALUE(b) FROM t1; COUNT(*) ANY_VALUE(b) @@ -10,6 +12,23 @@ SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; ANY_VALUE(a) b 1 10 3 20 +SELECT ANY_VALUE(b) FROM t1 GROUP BY b; +ANY_VALUE(b) +10 +20 +SELECT b, ANY_VALUE(a + 100) FROM t1 GROUP BY b; +b ANY_VALUE(a + 100) +10 101 +20 103 +SELECT b, ANY_VALUE(a * 2 + b) FROM t1 GROUP BY b; +b ANY_VALUE(a * 2 + b) +10 12 +20 26 +SELECT y, ANY_VALUE(x + z) FROM t3 GROUP BY y; +y ANY_VALUE(x + z) +10 11 +20 23 +30 35 SELECT b, (SELECT SUM(t2.d) + SUM(t1.a) FROM t2) AS sub FROM t1 GROUP BY b; b sub 10 603 @@ -20,3 +39,4 @@ b sub 20 603 DROP TABLE t1; DROP TABLE t2; +DROP TABLE t3; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test index 8f5fc65274bdd..3986ea0305e6f 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -1,16 +1,29 @@ CREATE TABLE t1 (a INT, b INT); CREATE TABLE t2 (c INT, d INT); +CREATE TABLE t3 (x INT, y INT, z INT); INSERT INTO t1 VALUES (1,10),(2,10),(3,20); 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); SET SQL_MODE=ONLY_FULL_GROUP_BY; +# Mixing real aggregate function and ANY_VALUE without GROUP BY SELECT COUNT(*), ANY_VALUE(b) FROM t1; + +# 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; +# 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; DROP TABLE t1; DROP TABLE t2; +DROP TABLE t3; From 521a12d67f3b57cde3bedac2c26234aec692377a Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sun, 14 Jun 2026 17:12:42 -0400 Subject: [PATCH 07/14] Undo COALESCE implementation of ANY_VALUE --- mysql-test/main/group_by.result | 5 +---- mysql-test/main/group_by.test | 1 + sql/item.cc | 8 +++----- sql/item_cmpfunc.cc | 14 -------------- sql/item_cmpfunc.h | 22 ---------------------- sql/item_create.cc | 23 ----------------------- sql/sql_lex.cc | 1 - sql/sql_lex.h | 1 - sql/sql_parse.cc | 1 - 9 files changed, 5 insertions(+), 71 deletions(-) diff --git a/mysql-test/main/group_by.result b/mysql-test/main/group_by.result index f4c1035622d8d..57a3cf97e94eb 100644 --- a/mysql-test/main/group_by.result +++ b/mysql-test/main/group_by.result @@ -1047,10 +1047,7 @@ 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; -(SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) -3 -7 -11 +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY 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 diff --git a/mysql-test/main/group_by.test b/mysql-test/main/group_by.test index 92594f37d2831..8009d976d191e 100644 --- a/mysql-test/main/group_by.test +++ b/mysql-test/main/group_by.test @@ -793,6 +793,7 @@ 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; diff --git a/sql/item.cc b/sql/item.cc index 607a87ea61126..e7cca88d9e7d9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6155,7 +6155,6 @@ 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 && !thd->lex->in_any_value && select->cur_pos_in_select_list != UNDEF_POS) { /* @@ -6698,7 +6697,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (check_ora_join(reference, outer_fixed)) goto error; if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && - !outer_fixed && !thd->lex->in_sum_func && !thd->lex->in_any_value && + !outer_fixed && !thd->lex->in_sum_func && select && select->cur_pos_in_select_list != UNDEF_POS && select->join) @@ -6734,10 +6733,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference) */ select_lex= context->select_lex; } - if (!thd->lex->in_sum_func && !thd->lex->in_any_value) + if (!thd->lex->in_sum_func) select_lex->set_non_agg_field_used(true); - else if (!thd->lex->in_any_value || - thd->lex->in_any_value->nest_level != select->nest_level) + else { if (outer_fixed) thd->lex->in_sum_func->outer_fields.push_back(this, thd->mem_root); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index a48911c4ce1a9..eaa705642d843 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -3724,20 +3724,6 @@ Type_ref_null Item_func_coalesce::ref_op(THD *thd) } -bool Item_func_any_value::fix_fields(THD *thd, Item **ref) -{ - in_any_value= thd->lex->in_any_value; - thd->lex->in_any_value= this; - nest_level= thd->lex->current_select->nest_level; - - if (Item_func::fix_fields(thd, ref)) - return 1; - - thd->lex->in_any_value= in_any_value; - - return 0; -} - /**************************************************************************** Classes and function for the IN operator diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 32f9078d8ee55..22a8cebca668f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1250,8 +1250,6 @@ class Item_func_interval :public Item_long_func class Item_func_coalesce :public Item_func_case_expression { public: - Item_func_coalesce(THD *thd, Item *a): - Item_func_case_expression(thd, a) {} Item_func_coalesce(THD *thd, Item *a, Item *b): Item_func_case_expression(thd, a, b) {} Item_func_coalesce(THD *thd, List &list): @@ -1291,26 +1289,6 @@ class Item_func_coalesce :public Item_func_case_expression }; -class Item_func_any_value final :public Item_func_coalesce -{ -public: - int8 nest_level; - Item_func_any_value *in_any_value; - Item_func_any_value(THD *thd, Item *a): - Item_func_coalesce(thd, a) {} - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING name= {STRING_WITH_LEN("any_value") }; - return name; - } - bool fix_fields(THD *thd, Item **ref) override; - -protected: - Item *shallow_copy(THD *thd) const override - { return get_item_copy(thd, this); } -}; - - /* Case abbreviations that aggregate its result field type by two arguments: IFNULL(arg1, arg2) diff --git a/sql/item_create.cc b/sql/item_create.cc index e2df1f626795f..f2716e643668a 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -168,19 +168,6 @@ class Create_func_aes_decrypt : public Create_native_func }; -class Create_func_any_value : public Create_func_arg1 -{ -public: - Item *create_1_arg(THD *thd, Item *arg1) override; - - static Create_func_any_value s_singleton; - -protected: - Create_func_any_value() = default; - ~Create_func_any_value() override = default; -}; - - class Create_func_kdf : public Create_native_func { public: @@ -3265,15 +3252,6 @@ Create_func_aes_decrypt::create_native(THD *thd, const LEX_CSTRING *name, } -Create_func_any_value Create_func_any_value::s_singleton; - -Item* -Create_func_any_value::create_1_arg(THD *thd, Item *arg1) -{ - return new (thd->mem_root) Item_func_any_value(thd, arg1); -} - - Create_func_kdf Create_func_kdf::s_singleton; Item* @@ -6352,7 +6330,6 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("ADD_MONTHS") }, BUILDER(Create_func_addmonths)}, { { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)}, { { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)}, - { { STRING_WITH_LEN("ANY_VALUE") }, BUILDER(Create_func_any_value)}, { { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)}, { { STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, { { STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 6188814f328a5..beda9a6b03fc3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1315,7 +1315,6 @@ void LEX::start(THD *thd_arg) builtin_select.nest_level_base= &unit; allow_sum_func.clear_all(); in_sum_func= NULL; - in_any_value= NULL; used_tables= 0; table_type= TABLE_TYPE_UNKNOWN; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f9107c12b8682..2e61fc1feb550 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3387,7 +3387,6 @@ struct LEX: public Query_tables_list Column_definition *last_field; Table_function_json_table *json_table; Item_sum *in_sum_func; - Item_func_any_value *in_any_value; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options Table_specification_st create_info; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a504674c42cb2..bcd6de564f550 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3072,7 +3072,6 @@ static bool do_execute_sp(THD *thd, sp_head *sp) thd->variables.select_limit= HA_POS_ERROR; thd->lex->in_sum_func= 0; // For Item_field::fix_fields() - thd->lex->in_any_value= 0; /* We never write CALL statements into binlog: From 98ede892d6f667cf3c5ae203ee47bb61f3fc0bcb Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Tue, 9 Jun 2026 21:07:03 -0400 Subject: [PATCH 08/14] MDEV-39932 Accept aggregated outer columns in subquery Under ONLY_FULL_GROUP_BY, a query that aggregated an outer column inside a subquery was wrongly rejected. This is fixed in Item_field::fix_outer_field by not appending the field to select->join->non_agg_fields when thd->lex->in_sum_func is not null. Furthermore, in Item_sum::check_sum_func, for all outer fields that are not aggregated at their SELECT_LEX's nest level, we append these fields to sel->join->non_agg_fields in order to ensure proper aggregation after the Item_sum's nest_level is known. --- mysql-test/main/group_by.result | 50 ++++++++++++++++++++++++++++++++- mysql-test/main/group_by.test | 46 +++++++++++++++++++++++++++++- sql/item.cc | 1 + sql/item_sum.cc | 17 +++++++---- 4 files changed, 106 insertions(+), 8 deletions(-) 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..2d74c7e11f453 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); } } } From 9525ad394ae650a3687c49ba85ca0cf87ddc143f Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sun, 14 Jun 2026 23:30:41 -0400 Subject: [PATCH 09/14] Implement ANY_VALUE as a 'real' aggregate function through subclassing Item_sum_min_max --- sql/item_sum.cc | 30 ++++++++++++++++++++++++++++++ sql/item_sum.h | 36 +++++++++++++++++++++++++++++++++++- sql/lex.h | 1 + sql/sql_yacc.yy | 15 ++++++++++++++- 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 2d74c7e11f453..5134299c1c8d4 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2615,6 +2615,36 @@ 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() {} + + /* 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..6f9bef3822b69 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, @@ -1255,6 +1255,40 @@ 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(FALSE) + { + } + + 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; + } + +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..19775b98b2f1c 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, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + | AVG_SYM '(' in_sum_expr ')' { $$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE); if (unlikely($$ == NULL)) From 5e0290b5feb0095918a07159b1335700c11b1034 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Sun, 14 Jun 2026 23:53:39 -0400 Subject: [PATCH 10/14] Add additional test cases to verify edge cases --- mysql-test/main/any_value.result | 19 +++++++++++++++++++ mysql-test/main/any_value.test | 26 +++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result index 3a29f63d25c95..7779131c592a0 100644 --- a/mysql-test/main/any_value.result +++ b/mysql-test/main/any_value.result @@ -1,13 +1,20 @@ 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)); INSERT INTO t1 VALUES (1,10),(2,10),(3,20); 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); SET SQL_MODE=ONLY_FULL_GROUP_BY; +SELECT ANY_VALUE(b) FROM t1; +ANY_VALUE(b) +10 SELECT COUNT(*), ANY_VALUE(b) FROM t1; COUNT(*) ANY_VALUE(b) 3 10 +SELECT c FROM t2 WHERE ANY_VALUE(d) = 200; +ERROR HY000: Invalid use of group function SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; ANY_VALUE(a) b 1 10 @@ -29,6 +36,14 @@ 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 @@ -37,6 +52,10 @@ SELECT b, (SELECT SUM(t2.d) + ANY_VALUE(t1.a) FROM t2) AS sub FROM t1 GROUP BY b b sub 10 601 20 603 +SELECT y, ANY_VALUE(z) FROM t3 GROUP BY y HAVING ANY_VALUE(z) = 20; +y ANY_VALUE(z) +20 20 DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; +DROP TABLE t4; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test index 3986ea0305e6f..f1bd84ac43da7 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -1,15 +1,31 @@ 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)); INSERT INTO t1 VALUES (1,10),(2,10),(3,20); 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); SET SQL_MODE=ONLY_FULL_GROUP_BY; -# Mixing real aggregate function and ANY_VALUE without GROUP BY +# +# Without GROUP BY +# + +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 +# + # Regular use in SELECT-list SELECT ANY_VALUE(a), b FROM t1 GROUP BY b; SELECT ANY_VALUE(b) FROM t1 GROUP BY b; @@ -19,11 +35,19 @@ 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; + DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; +DROP TABLE t4; From 32cda04b4e45eead9cca4e82c69f2e6fefe52c70 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Mon, 15 Jun 2026 00:10:31 -0400 Subject: [PATCH 11/14] Fix a minor indexing error in sql_yacc.yy, add ANY_VALUE_FUNC to is_aggr_sum_func() --- sql/item_sum.h | 1 + sql/sql_yacc.yy | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/item_sum.h b/sql/item_sum.h index 6f9bef3822b69..5dbcedd6d7d58 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -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: diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 19775b98b2f1c..ff28ef8d12f9e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11549,7 +11549,7 @@ sum_expr: } | ANY_VALUE_SYM '(' DISTINCT in_sum_expr ')' { - $$= new (thd->mem_root) Item_sum_any_value(thd, $3); + $$= new (thd->mem_root) Item_sum_any_value(thd, $4); if (unlikely($$ == NULL)) MYSQL_YYABORT; } From 57959eda0c3ffedfd84c71f0cc6f04721ca00f4f Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Mon, 15 Jun 2026 19:55:12 -0400 Subject: [PATCH 12/14] Modify any_value test case to include null rows and empty table --- mysql-test/main/any_value.result | 31 +++++++++++++++++++++++++------ mysql-test/main/any_value.test | 7 ++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result index 7779131c592a0..b995ad49738d5 100644 --- a/mysql-test/main/any_value.result +++ b/mysql-test/main/any_value.result @@ -1,36 +1,50 @@ +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)); -INSERT INTO t1 VALUES (1,10),(2,10),(3,20); +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); 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) -3 10 +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 -3 20 +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 103 +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 26 +20 NULL +30 NULL +40 48 SELECT y, ANY_VALUE(x + z) FROM t3 GROUP BY y; y ANY_VALUE(x + z) 10 11 @@ -48,13 +62,18 @@ 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 603 +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 +DROP TABLE t0; DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test index f1bd84ac43da7..ec6aaa499c133 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -1,8 +1,9 @@ +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)); -INSERT INTO t1 VALUES (1,10),(2,10),(3,20); +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); @@ -13,6 +14,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 @@ -26,6 +28,8 @@ 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; @@ -47,6 +51,7 @@ 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; +DROP TABLE t0; DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; From e6c8dc034c3fa1569db18ae15767f0d632e5727e Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Thu, 18 Jun 2026 00:06:32 -0400 Subject: [PATCH 13/14] Implement copy_or_same --- mysql-test/main/any_value.result | 17 ++++++++++++----- mysql-test/main/any_value.test | 11 ++++++----- sql/item_sum.cc | 10 ++++++++++ sql/item_sum.h | 3 ++- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result index b995ad49738d5..1839165ddbb6c 100644 --- a/mysql-test/main/any_value.result +++ b/mysql-test/main/any_value.result @@ -3,10 +3,12 @@ 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) @@ -73,8 +75,13 @@ b sub SELECT y, ANY_VALUE(z) FROM t3 GROUP BY y HAVING ANY_VALUE(z) = 20; y ANY_VALUE(z) 20 20 -DROP TABLE t0; -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE t3; -DROP TABLE t4; +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; diff --git a/mysql-test/main/any_value.test b/mysql-test/main/any_value.test index ec6aaa499c133..b78aa5a8177da 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -3,10 +3,12 @@ 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; @@ -51,8 +53,7 @@ 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; -DROP TABLE t0; -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE t3; -DROP TABLE t4; +# Rollup +SELECT x, y, ANY_VALUE(z) FROM t5 GROUP BY x, y WITH ROLLUP; + +DROP TABLE t0,t1,t2,t3,t4,t5; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5134299c1c8d4..d319a048c4c4c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2642,9 +2642,19 @@ void Item_sum_any_value::clear() 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 5dbcedd6d7d58..14c98fc8c8e4d 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1264,7 +1264,7 @@ class Item_sum_any_value final : public Item_sum_min_max { } Item_sum_any_value(THD *thd, Item_sum_any_value *item) - : Item_sum_min_max(thd, item), has_value(FALSE) + : Item_sum_min_max(thd, item), has_value(item->has_value) { } @@ -1278,6 +1278,7 @@ class Item_sum_any_value final : public Item_sum_min_max 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 From 86dfc73769ff8704238f0e3d9abe0ae92ddd80bd Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Fri, 19 Jun 2026 23:24:08 -0400 Subject: [PATCH 14/14] Add charset/collation/coercibility tests --- mysql-test/main/any_value.result | 9 +++++++++ mysql-test/main/any_value.test | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/mysql-test/main/any_value.result b/mysql-test/main/any_value.result index 1839165ddbb6c..b1e8a84b22da2 100644 --- a/mysql-test/main/any_value.result +++ b/mysql-test/main/any_value.result @@ -85,3 +85,12 @@ x y ANY_VALUE(z) 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 index b78aa5a8177da..a7137d03516c7 100644 --- a/mysql-test/main/any_value.test +++ b/mysql-test/main/any_value.test @@ -57,3 +57,17 @@ SELECT y, ANY_VALUE(z) FROM t3 GROUP BY y HAVING ANY_VALUE(z) = 20; 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;