This is an automated email from the ASF dual-hosted git repository.

rubenada pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 5e3326a0a8 [CALCITE-7731] Bound plain-notation expansion of DECIMAL 
literals to prevent parse-time OutOfMemoryError (follow-up, added more tests)
5e3326a0a8 is described below

commit 5e3326a0a8e7cec4c804f5e00c80b5510b61945d
Author: Suraj Rajan <[email protected]>
AuthorDate: Mon Aug 24 15:21:39 2026 +0530

    [CALCITE-7731] Bound plain-notation expansion of DECIMAL literals to 
prevent parse-time OutOfMemoryError (follow-up, added more tests)
---
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 24 ++++++++++++++++++++++
 .../org/apache/calcite/rex/RexBuilderTest.java     | 20 ++++++++++++++++++
 .../java/org/apache/calcite/sql/SqlNodeTest.java   | 22 ++++++++++++++++++++
 3 files changed, 66 insertions(+)

diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 8e2b6efb53..eb30ef3089 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -117,6 +117,7 @@
 import org.opentest4j.TestAbortedException;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -132,8 +133,10 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasToString;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
@@ -428,6 +431,27 @@ private static RelNode projectOfLiterals() {
         isLinux(expectedPreserved));
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7731";>[CALCITE-7731]
+   * Bound plain-notation expansion of DECIMAL literals to prevent parse-time
+   * OutOfMemoryError</a>. A DECIMAL literal whose plain-notation expansion 
would
+   * exceed the configured bound must not be converted to SQL text. */
+  @Test void testDecimalLiteralPlainNotationBoundInRelToSql() {
+    final RelBuilder b = relBuilder();
+    final RexBuilder rexBuilder = b.getRexBuilder();
+    // A literal with 20,000 digits comfortably exceeds the default 10,000
+    // digit bound, without requiring a multi-gigabyte allocation to build.
+    final BigDecimal outOfBound = new BigDecimal(BigInteger.TEN.pow(20_000));
+    final RelNode root = b
+        .scan("EMP")
+        .project(rexBuilder.makeExactLiteral(outOfBound))
+        .build();
+    final SqlDialect dialect = DatabaseProduct.CALCITE.getDialect();
+    final IllegalStateException e =
+        assertThrows(IllegalStateException.class, () -> toSql(root, dialect));
+    assertThat(e.getMessage(),
+        containsString("exceeds the configured plain-notation bound"));
+  }
+
   /** Parses a SQL query and converts it to a relational expression. */
   private static RelNode sqlToRel(String sql, SchemaPlus defaultSchema,
       SqlParser.Config parserConfig, Set<SqlLibrary> librarySet,
diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
index e23679eaf5..6637a2c776 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
@@ -772,6 +772,26 @@ private void checkDate(RexLiteral literal) {
     assertThat(rexLiteralHalfUp.getValue(), hasToString("12300"));
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7731";>[CALCITE-7731]
+   * Bound plain-notation expansion of DECIMAL literals to prevent parse-time
+   * OutOfMemoryError</a>. Casting to a DECIMAL type whose (dialect-permitted)
+   * negative scale would make the plain-notation expansion exceed the
+   * configured bound must fail rather than attempt the expansion. */
+  @Test void testDecimalWithNegativeScaleExceedingPlainNotationBound() {
+    final RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(
+            CustomTypeSystems.withMinScale(RelDataTypeSystem.DEFAULT,
+                typeName -> -20_000));
+    final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 3, 
-20_000);
+    final RexBuilder builder = new RexBuilder(typeFactory);
+    final BigDecimal value = new BigDecimal("123");
+    final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class, () -> {
+      builder.makeLiteral(value, type);
+    });
+    assertThat(e.getMessage(),
+        containsString("plain-notation expansion exceeds the configured 
bound"));
+  }
+
   /** Tests {@link DateString} year range. */
   @Test void testDateStringYearError() {
     try {
diff --git a/core/src/test/java/org/apache/calcite/sql/SqlNodeTest.java 
b/core/src/test/java/org/apache/calcite/sql/SqlNodeTest.java
index 220c5cb15b..75311d2612 100644
--- a/core/src/test/java/org/apache/calcite/sql/SqlNodeTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/SqlNodeTest.java
@@ -23,6 +23,8 @@
 import org.apache.calcite.util.TestUtil;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Strings;
+
 import org.hamcrest.CustomTypeSafeMatcher;
 import org.hamcrest.Matcher;
 import org.junit.jupiter.api.Test;
@@ -35,7 +37,9 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasSize;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /**
  * Test of {@link SqlNode} and other SQL AST classes.
@@ -74,6 +78,24 @@ class SqlNodeTest {
         isEqualsDeep("CAST(a AS ROW(field INTEGER))"));
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7731";>[CALCITE-7731]
+   * Bound plain-notation expansion of DECIMAL literals to prevent parse-time
+   * OutOfMemoryError</a>. A {@link SqlNumericLiteral} whose value's 
plain-notation
+   * expansion would exceed the configured bound must fail when unparsed, even 
if
+   * the literal was not created via the SQL parser. */
+  @Test void testNumericLiteralToValueExceedingPlainNotationBound() {
+    // A literal with a 20,001-digit fractional expansion comfortably exceeds
+    // the default 10,000 digit bound, without requiring a multi-gigabyte
+    // allocation to build.
+    final String digits = "0." + Strings.repeat("0", 20_000) + "1";
+    final SqlNumericLiteral literal =
+        SqlLiteral.createExactNumeric(digits, SqlParserPos.ZERO);
+    final IllegalArgumentException e =
+        assertThrows(IllegalArgumentException.class, literal::toValue);
+    assertThat(e.getMessage(),
+        containsString("exceeds the configured plain-notation bound"));
+  }
+
   private static Matcher<String> isEqualsDeep(String sqlExpected) {
     return new CustomTypeSafeMatcher<String>("isDeepEqual") {
       @Override protected boolean matchesSafely(String sqlActual) {

Reply via email to