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