Skip to content

[CALCITE-7750] Bound plain-notation expansion of DECIMAL literals in Primitive.checkOverflow - #5223

Open
shoemoney wants to merge 1 commit into
apache:mainfrom
shoemoney:fix/calcite-checkoverflow-bounded
Open

[CALCITE-7750] Bound plain-notation expansion of DECIMAL literals in Primitive.checkOverflow#5223
shoemoney wants to merge 1 commit into
apache:mainfrom
shoemoney:fix/calcite-checkoverflow-bounded

Conversation

@shoemoney

Copy link
Copy Markdown

Sibling of CALCITE-7731.

Bug: Primitive.checkOverflow calls BigDecimal.toPlainString on a value with scale < 0 without bounding the plain-notation expansion. A large negative scale can force toPlainString to materialize gigabytes and OOM, same pattern bounded elsewhere in CALCITE-7731.

Fix: Gate the toPlainString call with isBoundedDecimal. This mirrors SqlUtil.isBoundedDecimal in core but is inlined in linq4j to avoid a circular linq4j -> core dependency. The bound reads calcite.parser.maxDecimalLiteralPlainDigits (default 10000) and checks precision + abs(scale) <= limit. If exceeded, throw IllegalArgumentException before allocation.

Evidence: RED->GREEN verified. Both :linq4j:compileJava configurations succeed. Formatter blast radius is one file.

@mihaibudiu

Copy link
Copy Markdown
Contributor

The linter won't like your commit message. Have you tried to validate this locally?
Maybe you should just use 7731 again in the commit message

…Primitive.checkOverflow

Primitive.checkOverflow calls BigDecimal.toPlainString on a value with
scale < 0 without bounding the plain-notation expansion, the same
pattern bounded elsewhere in CALCITE-7731. A large negative scale can
force toPlainString to materialize gigabytes and OOM.

Gate the toPlainString call with isBoundedDecimal, mirroring
SqlUtil.isBoundedDecimal in core but inlined in linq4j to avoid a
circular linq4j -> core dependency. The bound reads
calcite.parser.maxDecimalLiteralPlainDigits (default 10000) and checks
precision + abs(scale) <= limit, throwing IllegalArgumentException
before allocation if exceeded.
@shoemoney
shoemoney force-pushed the fix/calcite-checkoverflow-bounded branch from c9a1bd9 to c9ed2c6 Compare August 27, 2026 20:25
@shoemoney

Copy link
Copy Markdown
Author

Fixed, commit message now uses [CALCITE-7731] in c9ed2c6.

@mihaibudiu mihaibudiu changed the title fix(calcite): gate checkOverflow toPlainString with isBoundedDecimal Bound plain-notation expansion of DECIMAL literals to prevent parse-time OutOfMemoryError Aug 27, 2026
@mihaibudiu

Copy link
Copy Markdown
Contributor

I have changed your PR title to match

@mihaibudiu
mihaibudiu requested a review from rubenada August 27, 2026 20:28
@rubenada

Copy link
Copy Markdown
Contributor

Is this really needed? Can we get into a "risky" scenario here, considering the checks introduced in CALCITE-7731 ?

@sonarqubecloud

Copy link
Copy Markdown

@rubenada

rubenada commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

@shoemoney , thanks for the patch.

Honestly, I don't know if we can realistically fall into a risky scenario for this class (looks like a "pattern-matching" fix). But the check won't hurt.
I have created a specific ticket for this: "[CALCITE-7750] Bound plain-notation expansion of DECIMAL literals in Primitive.checkOverflow". I have updated the PR title to reflect it.
Please use it on the commit message.

Also, please add some unit tests for this in PrimitiveTest.java, e.g. one for regression coverage, one reproducing the actual issue:

  @Test void testCharToDecimalCastWithinBounds() {
    assertThat(Primitive.charToDecimalCast("1.5", 5, 2),
        is(new BigDecimal("1.50")));
    assertThat(Primitive.charToDecimalCast("0", 38, 0),
        is(new BigDecimal("0")));
    // scale < 0 branch, well below the bound.
    assertThat(Primitive.charToDecimalCast("1000", 4, -3),
        is(new BigDecimal("1000")));
  }

  /** Test case for
   * <a href="https://issues.apache.org/jira/browse/CALCITE-7750">[CALCITE-7750]
   * Bound plain-notation expansion of DECIMAL literals in Primitive.checkOverflow</a>. */
  @Test void testCharToDecimalCastRejectsPathologicalScale() {
    IllegalArgumentException e =
        assertThrows(IllegalArgumentException.class, () ->
            Primitive.charToDecimalCast("1E10000", 1, -10_000));
    assertThat(e.getMessage(),
        containsString("plain-notation bound"));
  }

@rubenada rubenada changed the title Bound plain-notation expansion of DECIMAL literals to prevent parse-time OutOfMemoryError [CALCITE-7750] Bound plain-notation expansion of DECIMAL literals in Primitive.checkOverflow Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants