MDEV-40199: Fix range access for negative FLOAT/DOUBLE UNSIGNED constants#5290
MDEV-40199: Fix range access for negative FLOAT/DOUBLE UNSIGNED constants#5290wufengwind wants to merge 1 commit into
Conversation
…ants Field_num::get_mm_leaf() builds a range by storing the comparison value into the field. For an indexed FLOAT or DOUBLE UNSIGNED column, a negative string constant such as '-1' is stored as 0, so the optimizer can build an equality range for 0 even though row-by-row evaluation of the predicate is false. Detect negative comparison values for unsigned FLOAT and DOUBLE fields before storing the value into the field. Equality and less-than comparisons are impossible, while greater-than comparisons are left for normal predicate evaluation instead of creating a truncated lower bound. Add type_float tests that compare indexed and non-indexed evaluation for FLOAT and DOUBLE UNSIGNED columns with a negative string constant. Signed-off-by: Feng Wu <wufengwufengwufeng@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request prevents incorrect range optimization for indexed FLOAT and DOUBLE UNSIGNED columns when compared with negative constants by returning an impossible range for equality and less-than comparisons. The feedback suggests extending this fix to UNSIGNED DECIMAL columns (MYSQL_TYPE_NEWDECIMAL and MYSQL_TYPE_DECIMAL) as they are subject to the same clipping behavior and range construction issues.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (unsigned_flag && | ||
| (type() == MYSQL_TYPE_FLOAT || type() == MYSQL_TYPE_DOUBLE)) |
There was a problem hiding this comment.
The same clipping behavior and range construction bug also affects UNSIGNED DECIMAL columns (MYSQL_TYPE_NEWDECIMAL and MYSQL_TYPE_DECIMAL). When a negative constant is compared to an UNSIGNED DECIMAL column, it is clipped to 0 during save_in_field_no_warnings, which can lead to incorrect equality ranges being constructed for 0.
Consider extending this check to cover MYSQL_TYPE_NEWDECIMAL and MYSQL_TYPE_DECIMAL as well.
if (unsigned_flag &&
(type() == MYSQL_TYPE_FLOAT || type() == MYSQL_TYPE_DOUBLE ||
type() == MYSQL_TYPE_NEWDECIMAL || type() == MYSQL_TYPE_DECIMAL))There was a problem hiding this comment.
Thanks for the review. I checked this locally before extending the patch, but I could not reproduce the wrong-result behavior for DECIMAL UNSIGNED.
I tested both DECIMAL(10,2) UNSIGNED and DECIMAL(10,0) UNSIGNED with an index and compared indexed vs IGNORE INDEX execution for =, <, <=, >, and >= against the negative string constant '-1'. The indexed and non-indexed results matched in all cases. For equality, EXPLAIN showed a ref access on the decimal key with rows=0, and the query returned an empty result.
The code path also seems different from FLOAT/DOUBLE: Field_new_decimal::store_value() detects a negative value for an unsigned decimal field, sets an out-of-range warning/error, and returns a non-OK status. That lets range construction go through the existing truncated/impossible handling. The FLOAT/DOUBLE bug fixed here is that the negative value is clipped to 0 without the same error signal, so the optimizer can build an equality range for 0.
So I would prefer to keep this patch scoped to FLOAT/DOUBLE unless there is a separate DECIMAL reproducer.
Summary
Range construction for numeric fields stores the comparison value into the field. For FLOAT/DOUBLE UNSIGNED columns, a negative string constant such as
'-1'can be clipped to 0 during this step. The optimizer can then create an equality range for 0, so an indexed query returns rows that row-by-row evaluation and a table scan reject.This patch detects negative comparison values for unsigned FLOAT and DOUBLE fields before range construction stores the value into the field. Equality and less-than comparisons are treated as impossible. Greater-than comparisons fall back to normal predicate evaluation instead of using a clipped range bound.
Jira: https://jira.mariadb.org/browse/MDEV-40199
Test