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

mihaibudiu 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 34dde49036 [CALCITE-7782] Large string ARRAY validation is quadratic: 
81 GB allocated at 100,000 elements and no result after 20 minutes at 1,000,000
34dde49036 is described below

commit 34dde490366b99f0cd34ddb464068b81e70a0b0b
Author: Frank Chen <[email protected]>
AuthorDate: Mon Sep 14 23:48:37 2026 +0800

    [CALCITE-7782] Large string ARRAY validation is quadratic: 81 GB allocated 
at 100,000 elements and no result after 20 minutes at 1,000,000
---
 .../java/org/apache/calcite/sql/SqlBasicCall.java    |  8 ++++++++
 .../main/java/org/apache/calcite/sql/SqlCall.java    | 19 +++++++++++++++++++
 .../calcite/sql/validate/SqlValidatorUtil.java       |  9 ++++++++-
 .../org/apache/calcite/sql/SqlCallOperandsTest.java  | 20 ++++++++++++++++++++
 4 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java 
b/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java
index 8279924af1..ba1c752cf9 100755
--- a/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java
@@ -97,6 +97,14 @@ public SqlCall withExpanded(boolean expanded) {
     operandList = set(operandList, i, operand);
   }
 
+  @Override public void setOperandList(
+      List<? extends @Nullable SqlNode> operands) {
+    if (operands.size() != operandList.size()) {
+      throw new IllegalArgumentException("operand count must not change");
+    }
+    operandList = ImmutableNullableList.copyOf(operands);
+  }
+
   /** Sets the operator (or function) that is being called.
    *
    * <p>This method is used by the validator to set a more refined version of
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCall.java 
b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
index 1747272f62..b5f60102c7 100755
--- a/core/src/main/java/org/apache/calcite/sql/SqlCall.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
@@ -72,6 +72,25 @@ public void setOperand(int i, @Nullable SqlNode operand) {
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * Replaces all operands. Allows some rewrite by {@link SqlValidator}; use
+   * sparingly.
+   *
+   * <p>The default implementation delegates to {@link #setOperand(int, 
SqlNode)}.
+   * Subclasses may override this method to replace all operands more 
efficiently.
+   *
+   * @param operands New operands
+   */
+  public void setOperandList(
+      List<? extends @Nullable SqlNode> operands) {
+    if (operands.size() != operandCount()) {
+      throw new IllegalArgumentException("operand count must not change");
+    }
+    for (int i = 0; i < operands.size(); i++) {
+      setOperand(i, operands.get(i));
+    }
+  }
+
   @Override public SqlKind getKind() {
     return getOperator().getKind();
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index b4465ab434..f907f4ba14 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -1548,6 +1548,7 @@ private static void adjustTypeForMultisetConstructor(
     SqlCall call = sqlCallBinding.getCall();
     List<RelDataType> operandTypes = sqlCallBinding.collectOperandTypes();
     List<SqlNode> operands = call.getOperandList();
+    @Nullable List<SqlNode> adjustedOperands = null;
     RelDataType elementType;
     for (int i = 0; i < operands.size(); i++) {
       if (i % 2 == 0) {
@@ -1556,9 +1557,15 @@ private static void adjustTypeForMultisetConstructor(
         elementType = oddType;
       }
       if (!operandTypes.get(i).equalsSansFieldNames(elementType)) {
-        call.setOperand(i, castTo(operands.get(i), elementType));
+        if (adjustedOperands == null) {
+          adjustedOperands = new ArrayList<>(operands);
+        }
+        adjustedOperands.set(i, castTo(operands.get(i), elementType));
       }
     }
+    if (adjustedOperands != null) {
+      call.setOperandList(adjustedOperands);
+    }
   }
 
   /**
diff --git a/core/src/test/java/org/apache/calcite/sql/SqlCallOperandsTest.java 
b/core/src/test/java/org/apache/calcite/sql/SqlCallOperandsTest.java
index d987167134..f2a36927e2 100644
--- a/core/src/test/java/org/apache/calcite/sql/SqlCallOperandsTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/SqlCallOperandsTest.java
@@ -23,6 +23,7 @@
 
 import org.junit.jupiter.api.Test;
 
+import java.util.Arrays;
 import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -36,6 +37,25 @@
  *
  */
 public class SqlCallOperandsTest {
+  @Test void testSqlBasicCallSetOperandList() {
+    final SqlLiteral one =
+        SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO);
+    final SqlLiteral two =
+        SqlLiteral.createExactNumeric("2", SqlParserPos.ZERO);
+    final SqlLiteral three =
+        SqlLiteral.createExactNumeric("3", SqlParserPos.ZERO);
+    final SqlBasicCall call =
+        (SqlBasicCall) SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
+            SqlParserPos.ZERO, one, two);
+    final List<SqlNode> originalOperands = call.getOperandList();
+
+    call.setOperandList(Arrays.asList(two, three));
+
+    assertThat(call.getOperandList(), equalTo(Arrays.asList(two, three)));
+    // Bulk replacement preserves the snapshot behavior of setOperand.
+    assertThat(originalOperands, equalTo(Arrays.asList(one, two)));
+  }
+
   @Test void testSqlDeleteGetOperandsMatchWithSetOperand() {
     SqlDelete sqlDelete =
         new SqlDelete(SqlParserPos.ZERO, new SqlIdentifier("table1", 
SqlParserPos.ZERO),

Reply via email to