FrankChen021 opened a new pull request, #5281: URL: https://github.com/apache/calcite/pull/5281
## Jira Link [CALCITE-7805](https://issues.apache.org/jira/browse/CALCITE-7805) ## Changes Proposed Large string ARRAY validation repeatedly combines compatible character types. The least-restrictive path may call `createTypeWithCharsetAndCollation` even when the current `BasicSqlType` already has the requested charset and collation. Previously, `BasicSqlType.createWithCharsetAndCollation` always allocated a new `BasicSqlType` and `SerializableCharset`; type-factory canonicalization then reduced that new object back to the existing canonical type. Return the existing immutable `BasicSqlType` immediately when the requested charset is equal and the collation is the same instance. Requests that change either attribute keep the existing construction and canonicalization path. This is an unchanged-attribute fast path, not a cache. It does not depend on a previous invocation or retain derived types. This branch is based directly on `main` at `38413ece6` and is independent of the other allocation optimizations. ## Execution Path There are two relevant charset/collation operations in the benchmark, and this change applies only to the second one. ### 1. Creating a string literal: attributes change A SQL string literal starts from a character type with the default charset and `IMPLICIT` collation. `SqlLiteral.createSqlType` then assigns the literal's `COERCIBLE` collation: ```text CHAR(n), default charset, IMPLICIT -> createTypeWithCharsetAndCollation(default charset, COERCIBLE) -> attributes differ, so a derived type is required ``` The new condition is false on this path. This PR does not avoid that allocation. ### 2. Combining the column and literal: attributes are unchanged During least-restrictive inference, Calcite combines the string column's `IMPLICIT` collation with the literal's `COERCIBLE` collation. The SQL collation rules select `IMPLICIT`. The newly created common result type already has the default charset and `IMPLICIT` collation, but `leastRestrictiveSqlType` applies those same attributes again: ```text column: VARCHAR(...), IMPLICIT literal: CHAR(n), COERCIBLE -> common collation: IMPLICIT -> create common result type, already default charset + IMPLICIT -> createTypeWithCharsetAndCollation(default charset, IMPLICIT) ``` Before this change, the final call allocated an equivalent `BasicSqlType` and charset wrapper and then canonicalized it back to the original instance: ```text already-correct BasicSqlType -> allocate equivalent BasicSqlType -> allocate/wrap its charset -> canonize(newType) -> return the existing canonical type ``` With this change: ```text already-correct BasicSqlType -> detect the same charset and collation -> return this -> canonize(existingType) ``` This repeated least-restrictive operation is where the benchmark's allocation reduction comes from. ## Isolated Benchmark Druid's `InPlanningBenchmark.queryStringInSqlPlanOnly` was run against a Calcite 1.42.0 + CALCITE-7782 baseline and the same baseline with only this PR's effective `BasicSqlType` change applied. The removed no-op `SqlTypeFactoryImpl` refactoring was excluded. | String literals | Allocation before | Allocation after | Allocation saved | Change | Time before | Time after | |---:|---:|---:|---:|---:|---:|---:| | 100,000 | 1,450.76 MiB/op | 1,319.51 MiB/op | 131.24 MiB/op | -9.05% | 739.72 ms/op | 763.63 ms/op | | 1,000,000 | 14,703.47 MiB/op | 13,421.72 MiB/op | 1,281.76 MiB/op | -8.72% | 8.086 s/op | 8.142 s/op | The timing confidence intervals overlap, so these results establish the allocation reduction but do not establish a latency improvement or regression. Allocation is total allocated bytes per planning operation, not retained or peak heap. The benchmark generates ordered numeric strings (`'1'`, `'2'`, ...). The optimization itself is based on unchanged charset and collation rather than literal-value order, but the measurements describe this specific workload and should not be generalized to all string expressions. Benchmark parameters: `inSubQueryThreshold=2147483647`, `rowsPerSegment=500000`; 2 JMH forks, each with 2 one-second warmup iterations and 5 one-second measured iterations; GC profiler; Temurin 25.0.4.1. ## Validation - `./gradlew :core:test --tests org.apache.calcite.sql.type.SqlTypeFactoryTest :core:autostyleJavaCheck :core:checkstyleMain :core:checkstyleTest` (27 completed, 0 failed) - Isolated Druid JMH benchmark at 100,000 and 1,000,000 string literals -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
