1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-36716 A case expression with ROW arguments in THEN crashes

The patch for SYS_REFCURSOR (MDEV-20034) overrode these methods:
- Item_func_case_searched::check_arguments()
- Item_func_if::check_arguments()

to validate WHEN-style arguments (e.g. args[0] in case of IF) for being
able to return a boolean result.

However, this unintentionally removed the test for the THEN-style arguments
that they are not expressions of the ROW data type.

This led to a crash inside Type_handler_hybrid_field_type::aggregate_for_result
on a DBUG_ASSERT that arguments are not of the ROW data type.

Fix:

The fix restores blocking ROW expressions in the not supported cases,
to avoid the DBUG_ASSERT and to raise an SQL error instead.

Blocking ROW_RESULT expressions is done per Item_func_case_expression
descendant individually, instead of blocking any ROW_RESULT arguments
at the Item_func_case_expression level.

The fix is done taking into account the upcoming patch for associative arrays
(MDEV-34319). It should be possible to pass associative array expressions into
some hybrid type functions, where ROW type expressions are not possible.

As a side effect, some lecagy ER_OPERAND_COLUMNS changed to
a newer ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION

Changes in the top affected class Item_func_case_expression:

- item_func.h:
  Overriding Item_func_case_expression::check_arguments() to return false,
  without checking any arguments. Descendant validate arguments
  in a various different ways. No needs to block all non-scalar data type at
  this level, to prevent disallowing associative arrays.

Changes in descendants:

- item_cmpfunc.cc:
  Adding a test in Item_func_case_simple::aggregate_switch_and_when_arguments()
  preventing passing ROW_RESULT expression in predicant and WHEN in a
  simple CASE:
    CASE predicant WHEN when1 THEN .. WHEN when2 THEN .. END;
  This is not supported yet. Should be preferrably fixed before MDEV-34319.

- item_cmpfunc.cc:
  Calling args[0]->type_handler()->Item_hybrid_func_fix_attributes()
  from Item_func_nullif::fix_length_and_dec().
  This prevents a ROW expression to be passed to args[0] of NULLIF().
  But will allow to pass associative arrays.

  args[1] is still only checked to be comparable with args[0].
  No needs to add additional tests for it.

- item_cmpfunc.h:
  Adding a call for Item_hybrid_func_fix_attributes() in
  Item_func_case_abbreviation2::cache_type_info().
  This prevents calling the descendant functions with
  a ROW expression in combination with an explicit NULL
  in the THEN-style arguments (but will allow to pass associative arrays):

    IFNULL(row_expression, NULL)
    IFNULL(NULL, row_expression)
    IF(switch, row_expression, NULL)
    IF(switch, NULL, row_expression)
    NVL2(switch, row_expression, NULL)
    NVL2(switch, NULL, row_expression)
  Adding a THD* argument into involved methods.

- item_cmpfunc.h:
  Overriding Item_func_case_abbreviation2_switch::check_arguments() to
  check that the first argument in IF() and NVL2() can return bool.
  Removing Item_func_if::check_arguments(), as it become redundant.

- sql_type.cc:
  Fixing sql_type.cc not to disallow items[0] with ROW_RESULT.
  This makes it call Item_hybrid_func_fix_attributes() at the end,
  which block ROW arguments into THEN-style arguments of hybrid functions.
  But this will allow to pass Type_handler_assoc_array expressions.

- sql_type.cc:
  Changing Type_handler_row::Item_hybrid_func_fix_attributes to raise the
  ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION error instead of the DBUG_ASSERT.
This commit is contained in:
Alexander Barkov
2025-04-30 14:04:43 +04:00
parent 4fc1063796
commit c5d8b9963a
11 changed files with 277 additions and 29 deletions

View File

@ -232,8 +232,13 @@ public:
const Type_handler *b)
const override
{
DBUG_ASSERT(a == &type_handler_row);
DBUG_ASSERT(b == &type_handler_row);
/*
Allowed combinations:
ROW+ROW, NULL+ROW, ROW+NULL
*/
DBUG_ASSERT(a == &type_handler_row || a == &type_handler_null);
DBUG_ASSERT(b == &type_handler_row || b == &type_handler_null);
DBUG_ASSERT(a == &type_handler_row || b == &type_handler_row);
return &type_handler_row;
}
const Type_handler *aggregate_for_min_max(const Type_handler *a,
@ -1893,19 +1898,16 @@ aggregate_for_result(const LEX_CSTRING &funcname, Item **items, uint nitems,
bool treat_bit_as_number)
{
bool bit_and_non_bit_mixture_found= false;
uint32 max_display_length;
if (!nitems || items[0]->result_type() == ROW_RESULT)
if (!nitems)
{
DBUG_ASSERT(0);
set_handler(&type_handler_null);
return true;
}
set_handler(items[0]->type_handler());
max_display_length= items[0]->max_display_length();
for (uint i= 1 ; i < nitems ; i++)
{
const Type_handler *cur= items[i]->type_handler();
set_if_bigger(max_display_length, items[i]->max_display_length());
uint bit_count= (type_handler() == &type_handler_bit) +
(cur == &type_handler_bit);
uint null_count= (type_handler() == &type_handler_null) +
@ -1926,7 +1928,12 @@ aggregate_for_result(const LEX_CSTRING &funcname, Item **items, uint nitems,
}
}
if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_slonglong)
{
uint32 max_display_length= items[0]->max_display_length();
for (uint i= 1; i < nitems ; i++)
set_if_bigger(max_display_length, items[i]->max_display_length());
set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length));
}
return false;
}
@ -4899,6 +4906,20 @@ bool Type_handler_timestamp_common::
return false;
}
bool Type_handler_row::
Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &opname,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems)
const
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
name().ptr(), opname.str);
return true;
}
/*************************************************************************/
bool Type_handler::