Description:
During reprepare, copy_parameter_types() calls Item_param::copy_param_actual_type(), which tries to zero the DECIMAL buffer but uses memset(decimal_value.buf, 0, DECIMAL_BUFF_LENGTH);, i.e., only 9 bytes. A DECIMAL buffer holds 9 dec1 values (9×4 bytes). For parameters whose precision/scale spans beyond the second dec1 (e.g., DECIMAL(16,10)), decimal2string() walks into the uninitialized dec1, reads a negative value, and div_by_pow10 asserts x >= 0. If the precision/scale uses only the first dec1 or two, the garbage isn’t read, so no crash—hence the conditional nature.
How to repeat:
In debug version:
SET optimizer_trace="enabled=on";
CREATE TABLE t1 (dc2 DECIMAL(16,10));
INSERT INTO t1 VALUES (1.1111111111);
PREPARE s FROM 'SELECT dc2+? FROM t1';
SET @a=1.12345678901234;
EXECUTE s USING @a;
-- trigger reprepare
ALTER TABLE t1 COMMENT='force reprepare';
EXECUTE s USING @a; -- crash here
Suggested fix:
Zero the entire DECIMAL buffer in Item_param::copy_param_actual_type():
memset(decimal_value.buf, 0, DECIMAL_BUFF_LENGTH * sizeof(decimal_digit_t));
This prevents uninitialized dec1 from being read during optimizer-trace printing in reprepare.