sp_head::execute_procedure() and sp_head::execute_function() did not
check that Item_param could be passed as an actual parameter to a ROW type
formal parameter of a stored routine. Example:
CREATE PROCEDURE p0(OUT a ROW(a INT,b INT)) ...;
PREPARE s0 'CALL p0(?)';
EXECUTE p0 USING @a;
In case of passing a user variable as an OUT parameter it led to
a crash after executing routine instructions, when copying formal
OUT parameters to the bound actual parameters.
Fix:
Check cases when Item_param is being bound to a ROW type formal parameter.
Raise an error if so. The new check is done for all parameter modes:
IN, OUT, INOUT, for a consistent error message.
The new check is done before executing the routine instructions.
The problem described in the bug report happened because the code
did not test check_cols(1) after fix_fields() in a few places.
Additionally, fix_fields() could be called multiple times for SP variables,
because they are all fixed at a early stage in append_for_log().
Solution:
1. Adding a few helper methods
- fix_fields_if_needed()
- fix_fields_if_needed_for_scalar()
- fix_fields_if_needed_for_bool()
- fix_fields_if_needed_for_order_by()
and using it in many cases instead of fix_fields() where
the "fixed" status is not definitely known to be "false".
2. Adding DBUG_ASSERT(!fixed) into Item_splocal*::fix_fields()
to catch double execution.
3. Adding tests.
As a good side effect, the patch removes a lot of duplicate code (~60 lines):
if (!item->fixed &&
item->fix_fields(..) &&
item->check_cols(1))
return true;