Repository navigation
[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 - #5263
Conversation
…cated at 100,000 elements and no result after 20 minutes at 1,000,000
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved blocking issues were identified, and the focused tests and checks passed.
Pull request overview
Optimizes large ARRAY/MAP constructor validation by batching operand replacements and eliminating quadratic immutable-list copying.
Changes:
- Adds bulk operand replacement to
SqlCallandSqlBasicCall. - Applies required casts in one operation.
- Adds operand snapshot behavior tests.
File summaries
| File | Description |
|---|---|
core/src/test/java/org/apache/calcite/sql/SqlCallOperandsTest.java |
Tests replacement and snapshot behavior. |
core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java |
Batches constructor operand casts. |
core/src/main/java/org/apache/calcite/sql/SqlCall.java |
Adds the bulk replacement API. |
core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java |
Implements efficient bulk replacement. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
| * | ||
| * @param operands New operands | ||
| */ | ||
| public void setOperandList( |
There was a problem hiding this comment.
what happens if you use the BasicCall implementation here?
There was a problem hiding this comment.
SqlCall cannot use the SqlBasicCall implementation directly because it has no operandList field; other subclasses may store operands differently. The default loop preserves each subclass’s existing setOperand behavior, while SqlBasicCall overrides it to replace its immutable operand list in one copy.
|
This looks fine to me, but I still would like an answer to my question before I approve |
|
@mihaibudiu Thank you for reviewing and approving for this. May I know what's the estimated date for next release? |
|
This is a more appropriate question for the mailing lists. |
|
BTW: I think that one of the main obstacles for a faster release cycle is the lack of reviews and of volunteers helping with the release. If you can help with either of these, you can perhaps speed-up the process. |
|
I plan to merge this PR tomorrow unless there are objections |
|
BTW: in the future I think a shorter title would be sufficient |



Jira Link
CALCITE-7782
Changes Proposed
Validation of large ARRAY and MAP constructors may adjust many operands to a
common component type.
SqlValidatorUtil.adjustTypeForMultisetConstructorcurrently calls
SqlBasicCall.setOperandseparately for every required cast.Each
setOperandinvocation creates a new immutable copy of the completeoperand list. When most of
noperands require casts, this results inapproximately O(n²) copying.
This change preserves the explicit casts introduced by CALCITE-5948 while
installing the adjusted operands efficiently:
SqlCall.setOperandList, with a default implementation that delegatesto the existing per-operand setter;
SqlBasicCallto create one immutable operand-list copy;adjustTypeForMultisetConstructorand installthe resulting list once;
behavior.
Other
SqlCallimplementations retain their existing per-operand behaviorthrough the default implementation.
Reproducer
Apache Druid exposes the problem after rewriting a large string
INpredicateto a scalar function containing an ARRAY constructor:
Mixed-width string literals are assigned types such as
CHAR(1),CHAR(2),and
CHAR(6), then cast to a commonVARCHARcomponent type. Consequently,nearly every operand enters the replacement path.
Benchmark
The 81 GB value below is cumulative allocation during one benchmark operation,
not peak heap usage.
The benchmark used Apache Druid commit
d480d66e7fe52ef5daa46eb1af9f33f954be9b7c, JDK 25.0.4.1, JMH 1.37, andInPlanningBenchmark.queryStringFunctionInSql.Parameters:
inSubQueryThreshold = 2147483647rowsPerSegment = 500000At 100,000 literals, the change is approximately 5.1 times faster and reduces
cumulative allocation by approximately 98%.
The official 1.42.0 million-element operation was stopped after 20 minutes
without producing a JMH score. The patched operation completes in approximately
10 seconds.
Testing
SqlCallOperandsTestCalciteSqlOperatorTestCalciteSqlOperatorTestCoreSqlOperatorTestCoreSqlOperatorTest:core:checkstyleMain:core:checkstyleTestAll focused tests, style checks, and fork CI passed.