From 94ecccd4eacd32225258888c0c45ff2dc5364526 Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Mon, 29 Jun 2026 18:34:35 -0400 Subject: [PATCH 1/2] Create GROUPING function --- sql/item_func.cc | 1 + sql/item_func.h | 28 +++++++++++++++++++++++++++- sql/lex.h | 1 + sql/sql_yacc.yy | 12 ++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/sql/item_func.cc b/sql/item_func.cc index a3519b6d5ba20..bf1605f551b09 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -836,6 +836,7 @@ String *Item_int_func::val_str(String *str) return str; } +longlong Item_func_grouping::val_int() { return 0; } bool Item_func_connection_id::fix_length_and_dec(THD *thd) { diff --git a/sql/item_func.h b/sql/item_func.h index c482cdca44c44..7f3b6f7186c01 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -109,7 +109,8 @@ class Item_func :public Item_func_or_sum JSON_EXTRACT_FUNC, JSON_VALID_FUNC, ROWNUM_FUNC, CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider CASE_SIMPLE_FUNC, // Used by ColumnStore/spider, - DATE_FUNC, YEAR_FUNC, SUBSTR_FUNC, LEFT_FUNC + DATE_FUNC, YEAR_FUNC, SUBSTR_FUNC, LEFT_FUNC, + GROUPING_FUNC }; /* @@ -1330,6 +1331,31 @@ class Item_int_func :public Item_func }; +class Item_func_grouping final : public Item_int_func +{ + List args; + Dynamic_array min_rollup_levels; + +public: + Item_func_grouping(THD *thd, List &args): Item_int_func(thd, args), args(args), min_rollup_levels(thd->mem_root) {} + + + longlong val_int() override; + const Type_handler *type_handler() const override + { return &type_handler_slonglong; } + enum Functype functype() const override { return GROUPING_FUNC; } + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING sum_name= {STRING_WITH_LEN("grouping") }; + return sum_name; + } + +protected: + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; + + class Item_long_func: public Item_int_func { public: diff --git a/sql/lex.h b/sql/lex.h index 5e52cbfc2a6ce..24171abb3eec8 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -273,6 +273,7 @@ SYMBOL symbols[] = { { "GRANT", SYM(GRANT)}, { "GRANTS", SYM(GRANTS)}, { "GROUP", SYM(GROUP_SYM)}, + { "GROUPING", SYM(GROUPING_SYM)}, { "HANDLER", SYM(HANDLER_SYM)}, { "HARD", SYM(HARD_SYM)}, { "HASH", SYM(HASH_SYM)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 78045470d3510..5c51504182a61 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -565,6 +565,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token FULLTEXT_SYM %token GOTO_ORACLE_SYM /* Oracle-R */ %token GRANT /* SQL-2003-R */ +%token GROUPING_SYM %token GROUP_CONCAT_SYM %token JSON_ARRAYAGG_SYM %token JSON_OBJECTAGG_SYM @@ -1621,6 +1622,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); json_default_literal set_expr_misc select_or_expr + grouping_operation %type opt_vers_auto_part @@ -10482,6 +10484,7 @@ column_default_non_parenthesized_expr: MYSQL_YYABORT; } } + | grouping_operation | window_func_expr { if (!Lex->select_stack_top) @@ -11540,6 +11543,15 @@ udf_expr: } ; +grouping_operation: + GROUPING_SYM '(' expr_list ')' + { + $$= new (thd->mem_root) Item_func_grouping(thd, *$3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + ; + sum_expr: AVG_SYM '(' in_sum_expr ')' { From 257f8c946482d25fed683ff2b7eb86544a51be2f Mon Sep 17 00:00:00 2001 From: Jaeheon Shim Date: Tue, 30 Jun 2026 22:09:08 -0400 Subject: [PATCH 2/2] Implement val_int of Item_func_grouping by tracking rollup level --- sql/item_func.cc | 44 ++++++++++++++++++++++++++++++++++++++- sql/item_func.h | 9 ++++---- sql/share/errmsg-utf8.txt | 2 ++ sql/sql_select.cc | 20 ++++++++++++++++++ sql/sql_select.h | 4 +++- 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/sql/item_func.cc b/sql/item_func.cc index bf1605f551b09..0215f73140cb7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -836,7 +836,49 @@ String *Item_int_func::val_str(String *str) return str; } -longlong Item_func_grouping::val_int() { return 0; } + +bool Item_func_grouping::setup_rollup(THD *thd, ORDER *group_list, + uint send_group_parts) +{ + if (!(min_rollup_levels= thd->alloc(arg_count))) + { + return true; + } + + for (uint i= 0; i < arg_count; i++) + { + uint gp= 0; + ORDER *g; + for (g= group_list; g; g= g->next, gp++) + { + if (args[i]->eq(*g->item, 0)) + break; + } + if (!g) + { + my_error(ER_FIELD_IN_GROUPING_NOT_GROUP_BY, MYF(0), + args[i]->full_name()); + return true; + } + min_rollup_levels[i]= gp; + } + + return false; +} + + +longlong Item_func_grouping::val_int() +{ + longlong result= 0; + for (uint i= 0; i < arg_count; i++) + { + if (current_level <= min_rollup_levels[i]) + { + result+= 1ULL << (arg_count - i - 1); + } + } + return result; +} bool Item_func_connection_id::fix_length_and_dec(THD *thd) { diff --git a/sql/item_func.h b/sql/item_func.h index 7f3b6f7186c01..97be8a90d2f78 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1333,13 +1333,14 @@ class Item_int_func :public Item_func class Item_func_grouping final : public Item_int_func { - List args; - Dynamic_array min_rollup_levels; + uint *min_rollup_levels; + uint current_level; public: - Item_func_grouping(THD *thd, List &args): Item_int_func(thd, args), args(args), min_rollup_levels(thd->mem_root) {} - + Item_func_grouping(THD *thd, List &args) : Item_int_func(thd, args) {} + bool setup_rollup(THD *thd, ORDER *group_list, uint send_group_parts); + void set_current_rollup_level(uint level) { current_level= level; } longlong val_int() override; const Type_handler *type_handler() const override { return &type_handler_slonglong; } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 611ca88b2d130..2f0937ca2374e 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12406,3 +12406,5 @@ ER_WARN_QB_NAME_PATH_VIEW_NOT_FOUND eng "Hint %s is ignored. `%s` required at element #%u of the path is not found in the target query block." ER_WARN_QB_NAME_PATH_NOT_SUPPORTED_INSIDE_VIEW eng "Hint %s is ignored. QB_NAME hints with path are not supported inside view definitions." +ER_FIELD_IN_GROUPING_NOT_GROUP_BY 42000 + eng "Argument '%-.192s' of GROUPING function is not in GROUP BY" \ No newline at end of file diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b2c6929f20311..117add4574f1f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -30588,6 +30588,14 @@ bool JOIN::rollup_init() Item *item; while ((item= it++)) { + if (item->type() == Item::FUNC_ITEM && + ((Item_func *) item)->functype() == Item_func::GROUPING_FUNC) + { + Item_func_grouping *grouping= (Item_func_grouping *) item; + if (grouping->setup_rollup(thd, group_list, send_group_parts)) + return true; + grouping_funcs.push_back(grouping, thd->mem_root); + } ORDER *group_tmp; bool found_in_group= 0; @@ -30803,6 +30811,16 @@ bool JOIN::rollup_make_fields(List &fields_arg, List &sel_fields, return 0; } +void JOIN::rollup_set_level(uint level) +{ + List_iterator it(grouping_funcs); + Item_func_grouping *grouping; + while ((grouping= it++)) + { + grouping->set_current_rollup_level(level); + } +} + /** Send all rollup levels higher than the current one to the client. @@ -30827,6 +30845,7 @@ int JOIN::rollup_send_data(uint idx) uint i; for (i= send_group_parts ; i-- > idx ; ) { + rollup_set_level(i); int res= 0; /* Get reference pointers to sum functions in place */ copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]); @@ -30840,6 +30859,7 @@ int JOIN::rollup_send_data(uint idx) send_records++; } } + rollup_set_level(send_group_parts); /* Restore ref_pointer_array */ set_items_ref_array(current_ref_ptrs); return 0; diff --git a/sql/sql_select.h b/sql/sql_select.h index 23a927bfcb14e..ad142b957ee70 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1812,7 +1812,8 @@ class JOIN :public Sql_alloc bool subq_exit_fl; ROLLUP rollup; ///< Used with rollup - + List grouping_funcs; + bool mixed_implicit_grouping; bool select_distinct; ///< Set if SELECT DISTINCT /** @@ -2087,6 +2088,7 @@ class JOIN :public Sql_alloc Item_sum ***func); int rollup_send_data(uint idx); int rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param, TABLE *table); + void rollup_set_level(uint level); void join_free(); /** Cleanup this JOIN, possibly for reuse */ void cleanup(bool full);