Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions sql/item_func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,49 @@ String *Item_int_func::val_str(String *str)
}


bool Item_func_grouping::setup_rollup(THD *thd, ORDER *group_list,
uint send_group_parts)
{
if (!(min_rollup_levels= thd->alloc<uint>(arg_count)))
{
return true;
}
Comment on lines +840 to +846

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The GROUPING() function returns a 64-bit integer bitmask. If arg_count exceeds 64, constructing the bitmask in val_int() will cause undefined behavior due to shifting by 64 or more bits (e.g., 1ULL << (arg_count - i - 1)). To prevent this, please add a check to limit the number of arguments to at most 64.

bool Item_func_grouping::setup_rollup(THD *thd, ORDER *group_list,
                                      uint send_group_parts)
{
  if (arg_count > 64)
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), "grouping");
    return true;
  }

  if (!(min_rollup_levels= thd->alloc<uint>(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;
}
Comment on lines +870 to +881

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure defensive programming and avoid potential null pointer dereferences (e.g., if setup_rollup was not called or failed), we should check if min_rollup_levels is null. Additionally, using bitwise OR (|=) on an unsigned ulonglong accumulator is more idiomatic for constructing bitmasks and avoids signed overflow undefined behavior when the MSB (bit 63) is set.

longlong Item_func_grouping::val_int()
{
  if (!min_rollup_levels)
    return 0;

  ulonglong result= 0;
  for (uint i= 0; i < arg_count; i++)
  {
    if (current_level <= min_rollup_levels[i])
    {
      result|= 1ULL << (arg_count - i - 1);
    }
  }
  return (longlong) result;
}


bool Item_func_connection_id::fix_length_and_dec(THD *thd)
{
if (Item_long_func::fix_length_and_dec(thd))
Expand Down
29 changes: 28 additions & 1 deletion sql/item_func.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/*
Expand Down Expand Up @@ -1330,6 +1331,32 @@ class Item_int_func :public Item_func
};


class Item_func_grouping final : public Item_int_func
{
uint *min_rollup_levels;
uint current_level;

public:
Item_func_grouping(THD *thd, List<Item> &args) : Item_int_func(thd, args) {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The member variables min_rollup_levels and current_level are not initialized in the constructor. In C++, primitive and pointer member variables are not automatically initialized and will contain garbage values. If val_int() or other methods are called before these are explicitly set, it will lead to undefined behavior or a server crash. Please initialize them in the constructor's initializer list.

  Item_func_grouping(THD *thd, List<Item> &args)
    : Item_int_func(thd, args), min_rollup_levels(NULL), current_level(0) {}


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; }
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<Item_func_grouping>(thd, this); }
};


class Item_long_func: public Item_int_func
{
public:
Expand Down
1 change: 1 addition & 0 deletions sql/lex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)},
Expand Down
2 changes: 2 additions & 0 deletions sql/share/errmsg-utf8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
20 changes: 20 additions & 0 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -30803,6 +30811,16 @@ bool JOIN::rollup_make_fields(List<Item> &fields_arg, List<Item> &sel_fields,
return 0;
}

void JOIN::rollup_set_level(uint level)
{
List_iterator<Item_func_grouping> 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.

Expand All @@ -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]);
Expand All @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion sql/sql_select.h
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,8 @@ class JOIN :public Sql_alloc
bool subq_exit_fl;

ROLLUP rollup; ///< Used with rollup

List<Item_func_grouping> grouping_funcs;

bool mixed_implicit_grouping;
bool select_distinct; ///< Set if SELECT DISTINCT
/**
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions sql/sql_yacc.yy
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> FULLTEXT_SYM
%token <kwd> GOTO_ORACLE_SYM /* Oracle-R */
%token <kwd> GRANT /* SQL-2003-R */
%token <kwd> GROUPING_SYM
%token <kwd> GROUP_CONCAT_SYM
%token <rwd> JSON_ARRAYAGG_SYM
%token <rwd> JSON_OBJECTAGG_SYM
Expand Down Expand Up @@ -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 <num> opt_vers_auto_part

Expand Down Expand Up @@ -10482,6 +10484,7 @@ column_default_non_parenthesized_expr:
MYSQL_YYABORT;
}
}
| grouping_operation
| window_func_expr
{
if (!Lex->select_stack_top)
Expand Down Expand Up @@ -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 ')'
{
Expand Down