Skip to content

MDEV-40199: Fix range access for negative FLOAT/DOUBLE UNSIGNED constants#5290

Draft
wufengwind wants to merge 1 commit into
MariaDB:mainfrom
wufengwind:fix-unsigned-real-negative-range
Draft

MDEV-40199: Fix range access for negative FLOAT/DOUBLE UNSIGNED constants#5290
wufengwind wants to merge 1 commit into
MariaDB:mainfrom
wufengwind:fix-unsigned-real-negative-range

Conversation

@wufengwind

Copy link
Copy Markdown

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

./mariadb-test-run.pl --suite=main --vardir=/tmp/mariadb-mtr-type-float type_float

…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>
@CLAassistant

CLAassistant commented Jun 29, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

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.

Comment thread sql/opt_range.cc
Comment on lines +9668 to +9669
if (unsigned_flag &&
(type() == MYSQL_TYPE_FLOAT || type() == MYSQL_TYPE_DOUBLE))

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 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))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants