Skip to content

Conversation

@kumarUjjawal
Copy link
Contributor

Which issue does this PR close?

Rationale for this change

SELECT ROUND('999.9'::DECIMAL(4,1)) incorrectly returned 100.0 instead of 1000.

When rounding a decimal value causes a carry-over that increases the number of digits (e.g., 999.9 → 1000.0), the result overflows the original precision constraint. The overflow was silently truncated during display, producing incorrect results.

What changes are included in this PR?

  • Increased the precision by 1 for all decimal types (capped at max precision)
  • Added and udpated slt tests

Are these changes tested?

  • Added new sqllogictest case for the specific bug scenario
  • Updated 2 existing tests with new expected precision values
  • All existing tests pass

Are there any user-facing changes?

Aspect Before After
ROUND('999.9'::DECIMAL(4,1)) 100.0 (wrong) 1000 (correct)
Return type Decimal128(4, 1) Decimal128(5, 1)

The return type precision is now increased by 1 to prevent overflow. This matches PostgreSQL behavior where ROUND can increase the number of digits before the decimal point when carry-over occurs.

Users who depend on exact output precision may need to explicitly cast:

SELECT CAST(ROUND(...) AS DECIMAL(p, s))

@github-actions github-actions bot added sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation labels Jan 21, 2026
@kumarUjjawal
Copy link
Contributor Author

Other options considered:

  1. Increase output precision by 1 (PostgreSQL behavior): The return_type should increase precision by 1 to accommodate potential carry-over from rounding. For DECIMAL(p, s) with decimal_places=0, return DECIMAL(p+1, s).

  2. Return error on overflow: Keep same precision but return an error when the rounded value exceeds the precision (current code uses mul_checked but the overflow happens silently in display).

  3. Dynamic precision based on scale reduction: When reducing scale (e.g., from 1 to 0), calculate the minimum precision needed.

I chose option 1 as the most user friendly, I would like to hear if there are other suggestion for this fix.

@Jefffrey
Copy link
Contributor

Other options considered:

1. Increase output precision by 1 (PostgreSQL behavior): The `return_type` should increase precision by 1 to accommodate potential carry-over from rounding. For `DECIMAL(p, s)` with `decimal_places=0`, return `DECIMAL(p+1, s)`.

2. Return error on overflow: Keep same precision but return an error when the rounded value exceeds the precision (current code uses mul_checked but the overflow happens silently in display).

3. Dynamic precision based on scale reduction: When reducing scale (e.g., from 1 to 0), calculate the minimum precision needed.

I chose option 1 as the most user friendly, I would like to hear if there are other suggestion for this fix.

Option 1 sounds good. Option 2 would be too strict, not sure I fully understand what option 3 means here.

One thing to keep in mind is we still need to consider edge case where precision is already maxed out. Would this bug occur again?

@kumarUjjawal
Copy link
Contributor Author

Other options considered:

1. Increase output precision by 1 (PostgreSQL behavior): The `return_type` should increase precision by 1 to accommodate potential carry-over from rounding. For `DECIMAL(p, s)` with `decimal_places=0`, return `DECIMAL(p+1, s)`.

2. Return error on overflow: Keep same precision but return an error when the rounded value exceeds the precision (current code uses mul_checked but the overflow happens silently in display).

3. Dynamic precision based on scale reduction: When reducing scale (e.g., from 1 to 0), calculate the minimum precision needed.

I chose option 1 as the most user friendly, I would like to hear if there are other suggestion for this fix.

Option 1 sounds good. Option 2 would be too strict, not sure I fully understand what option 3 means here.

One thing to keep in mind is we still need to consider edge case where precision is already maxed out. Would this bug occur again?

You were right to point that, i did some more test and found out it was overflowing after 38 digits, I handled that as an error.


// Validate the result fits within the precision
// The max value for a given precision is 10^precision - 1
let max_value = ten.pow_checked(precision as u32).map_err(|_| {
Copy link
Contributor

Choose a reason for hiding this comment

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

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

Labels

functions Changes to functions implementation sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

round decimal incorrect result

2 participants