HIVE-29641: Upgrade Calcite to 1.42.0#6523
Conversation
…ange in udf_between.q The plan difference comes from: 1.33 BETWEEN -> RexSimplify: 100 <= x AND x <= 200 -> HivePointLookupOptimizerRule: x BETWEEN 100, 200 -> SearchTransformer: no-op NOT BETWEEN -> RexSimplify: x < 100 OR x > 200 -> HivePointLookupOptimizerRule: x NOT BETWEEN 100, 200 -> SearchTransformer: no-op vs 1.42 BETWEEN -> RexSimpliy: Sarg x [100..200] -> HivePointLookupOptimizerRule: no-op -> SearchTransformer: x BETWEEN 100, 200 NOT BETWEEN -> RexSimpliy: Sarg x (-inf..100, 200..+inf) -> HivePointLookupOptimizerRule: no-op -> SearchTransformer: x < 100 OR x > 200 Thus, we need to adjust SearchTransformer to detect the last case and generate: x NOT BETWEEN 100, 200
… to avoid plan change in udf_between.q) solved 1 plan change but "broke" 8 other tests (see below *); so instead, simply revert the change in SearchTransformer and adjust udf_between.q.out. The "identify NOT BETWEEN expression in SearchTransformer" (to deal with "regression" caused by ""[CALCITE-7194] Simplify comparisons between function calls and literals to SEARCH (1.41)") can be handled in a separate, dedicated PR. * testCliDriver[vector_between_columns] – org.apache.hadoop.hive.cli.split28.TestMiniLlapLocalCliDriver4s testCliDriver[external_jdbc_table_perf] – org.apache.hadoop.hive.cli.split8.TestMiniLlapLocalCliDriver48s testCliDriver[filter_numeric] – org.apache.hadoop.hive.cli.split8.TestMiniLlapLocalCliDriver3s testCliDriver[correlationoptimizer8] – org.apache.hadoop.hive.cli.split5.TestMiniLlapLocalCliDriver5s testCliDriver[vector_between_in] – org.apache.hadoop.hive.cli.split29.TestMiniLlapLocalCliDriver36s testCliDriver[stats_histogram_null] – org.apache.hadoop.hive.cli.split22.TestMiniLlapLocalCliDriver28s testCliDriver[join34] – org.apache.hadoop.hive.cli.split11.TestMiniLlapLocalCliDriver2s testCliDriver[join35] – org.apache.hadoop.hive.cli.split11.TestMiniLlapLocalCliDriver
…r query24, 30, 38, 54, 81, 87 due to [CALCITE-7083] RelMdDistinctRowCount aggregates implementation problems (1.41) Test adjustments: udtf_explode.q.out plan with GROUP BY + SORT fixed due to [CALCITE-6748] RelToSqlConverter returns the wrong result when Aggregate is on Sort (1.39) (and also array changes due to [CALCITE-6417] Map value constructor and Array value constructor unparsed incorrectly for HiveSqlDialect (1.38)) Test adjustments: vectorized_dynamic_partition_pruning.q.out Some IS NOT NULL simplifications added, e.g. lossless cast double to int to double + division by non-zero: UDFToDouble(UDFToInteger((hr / 2.0D))) is not null => hr is not null due to [CALCITE-5769] Optimizing 'CAST(e AS t) IS NOT NULL' to 'e IS NOT NULL' (1.35) (only if "lossless" cast) Some IS NOT NULL simplifications removed, e.g. not lossless cast str to double: UDFToDouble(hr) is not null => (UDFToDouble(hr) * 2.0D) is not null due to [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42) (non-lossless cast is considered a non-safe operation, so no simplification is applied) Test adjustments: subquery_scalar.q.out Changes around IS [NOT] NULL simplifications: [CALCITE-5769] Optimizing 'CAST(e AS t) IS NOT NULL' to 'e IS NOT NULL' (1.35) (only if "lossless" cast) [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42) Also if(..) predicate expanded into equivalent OR due to widened SafeRexVisitor's allowlist (enough that simplifyBooleanCaseGeneric could be applied) [CALCITE-7032] Simplify 'NULL>ALL (ARRAY[1,2,NULL])' to 'NULL' (1.41) As a consequence of this simplification, there's a row-count delta in the expression (fallout in Hive's selectivity heuristic) Add new "case CHAR_LENGTH" in SqlFunctionConverter#buildAST, since a new SqlKind.CHAR_LENGTH has been added (previously length functions had SqlKind.OTHER_FUNCTION) via [CALCITE-6182] Add LENGTH/LEN function (enabled in Snowflake library) (1.37) Problem observed in several tests, where lenght(x) function not being evaluated (returning 'length' instead of the actual value), e.g. TestJdbcDriver2#testExprCol: Illegal conversion to int from column 2 [length] java.sql.SQLException: Illegal conversion to int from column 2 [length] at org.apache.hive.jdbc.HiveBaseResultSet.getInt(HiveBaseResultSet.java:445) at org.apache.hive.jdbc.TestJdbcDriver2.testExprCol(TestJdbcDriver2.java:2217)
…adoc ASM error "Error in ASM processing class org/apache/hive/druid/org/apache/calcite/runtime/SqlFunctions.class: Index 65536 out of bounds for length 334"
…464 (introduce in 1.38) changed the default behavior to match MS-SQL-Server-style algorithm, which can cause a drop in the scale computation, hence a precision loss in certain cases. We override this method to keep the "old behavior" (pre-CALCITE-6464); an alternative could be not overridding it (and keep the new Calcite default MS-SQL-style decimal-divide semantics), but that would lead to "regressions" (precision loss) and would require test adjustments.
… to fix the ASM error on Jenkins)
plus
Add httpclient5/httpcore5 as runtime dependencies of hive-exec to fix NoClassDefFoundError after Calcite 1.42 upgrade
The Calcite 1.42 upgrade pulls in avatica-core 1.28.0, which adds runtime
dependencies on httpclient5 (5.5) and httpcore5 (5.3.5). These show up
indirectly: BuiltInConnectionProperty's static initializer references
org.apache.hc.core5.util.Timeout, and that class is loaded the first time
any Calcite planner / driver code runs - which happens on every query
compilation through CalcitePlanner.logicalPlan, not just remote-HTTP
transport.
Avatica is shaded into hive-exec (see the build-exec-bundle execution in
ql/pom.xml's artifactSet). The shade plugin removes avatica from the
generated dependency-reduced-pom but does not promote avatica's transitive
dependencies, so consumers of hive-exec did not pick up httpclient5 /
httpcore5 from anywhere, and any test or runtime that triggered Calcite
planning failed with:
java.lang.NoClassDefFoundError: org/apache/hc/core5/util/Timeout
at org.apache.calcite.avatica.BuiltInConnectionProperty.<clinit>(...)
at org.apache.calcite.avatica.ConnectionConfigImpl.transparentReconnectionEnabled(...)
at org.apache.calcite.avatica.AvaticaConnection.<init>(...)
...
at org.apache.hadoop.hive.ql.parse.CalcitePlanner.logicalPlan(...)
The failure was masked on Jenkins (and on machines with a fresh full
build) because itests modules transitively depend on hive-iceberg-handler,
and iceberg/iceberg-catalog/pom.xml declares httpclient5 / httpcore5
directly for the REST catalog client. So those jars happened to land on
the test classpath via a different path. On a partial local rebuild the
chain breaks and the error surfaces.
Declare httpclient5 and httpcore5 as direct dependencies of hive-exec
(versions inherited from the parent pom's dependencyManagement) so they
flow transitively to every consumer regardless of whether iceberg-handler
is in the picture. Compile scope (the default) is required - test scope
would not propagate to consumers, and the classes are needed at production
runtime, not only under tests.
Add asm-tree 9.9.1 dependency for druid handler shade plugin (still trying to fix the ASM error on Jenkins)
temp diagnose actions on Jenkinsfile to investigate ASM error
Condition changed simply as swap of conjunctions (same semantics): old filterExpr: (((d < 3.0) or (d > 7.0)) and (e > 0)) new filterExpr: ((e > 0) and ((d < 3.0) or (d > 7.0))) However, this led to a change on "Statistics: Num rows" (and that in turn led to a change on minReductionHashAggr value). The change on statistics num rows comes from the fact that StatsRulesProcFactory.evaluateExpression treats AND non-commutatively: child processing order matters due to the iterative rounding to long on each step: - Old order: 15 * sel((d<3 OR d>7)) rounded → then * sel(e>0) rounded → 8 - New order: 15 * sel(e>0) rounded → then * sel((d<3 OR d>7)) rounded → 9 Then minReductionHashAggr changed too, due to this numRows change, see SetHashGroupByMinReduction: minReductionHashAggrFactor = 1f - ((float) ndvProduct / numRows) Test adjustments decimal_udf.q.out: some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations. [CALCITE-7145] RexSimplify should not simplify IS NULL(10/0) (1.42) [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42) [CALCITE-7295] RexSimplify should simplify a division with a NULL argument (1.42) — added a new x/NULL → NULL rule, but only when the surrounding context is "safe". Adjust test file jdbc_project_pushdown.q.out: Calcite SQL unparse for SUBSTRING/SUBSTR changed SUBSTRING(s FROM start FOR length) => SUBSTRING(s, start, length) due to [CALCITE-5677] SUBSTR signature incorrect for BigQuery (1.35) Note: The new emitted SQL is valid in every dialect that Hive is realistically going to push down to (Derby, MySQL, PostgreSQL, MS SQL, Oracle, BigQuery all accept comma-separated SUBSTRING/SUBSTR). It's actually more dialect-portable than the old form: the old SUBSTRING(x FROM y FOR z) is SQL-92/99 syntax that some older or stricter dialects don't accept, while comma-form is universal. CALCITE-5677 was made specifically to fix the BigQuery breakage;
…emoving Calcite relocation
…s, double literals formatting Adjust test files timestamp.q.out, plan change due to [CALCITE-4334] LITERAL_AGG, an aggregate function that returns a constant value (1.35) (plus AggregateProjectPullUpConstantsRule) Test adjustments auto_join13.q.out, join13.q.out: some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations. In this case operations involving "non-lossless" casts cannot be simplified, so IS_NOT_NULL(PLUS(CAST(_col0), CAST(_col2))) => IS_NOT_NULL(_col0) AND IS_NOT_NULL(_col2) does not happen anymore (so they can't be pushed down), hence residual filter remains. [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42)
…tional expressions as unique" (1.37), RelMdUniqueKeys can return now
a set containing a single, empty set in cases like an empty Values or Values with 1 tuple; so we need to consider this circumstance when checking
the uniqueKeys of Aggregate's input in HiveRelFieldTrimmer#generateNewGroupset, otherwise it can convert an HiveAggregate(group=[{0}], COUNT)
into HiveAggregate(group=[{}], COUNT) which is NOT the same semantics when the input is an empty Values (empty resultset vs 0).
Issue seen on TestMiniLlapLocalCliDriver with empty_result.q which was incorrectly generating a Project+Agg+EmptyValues plan instead
of simply EmptyValues before this patch:
select count(a1) from t1 where 1=0 group by a1 order by a1;
< HiveProject(_c0=[$0])
< HiveAggregate(group=[{}], agg#0=[count($0)])
< HiveValues(tuples=[[]])
---
> HiveValues(tuples=[[]])
…onstant_expr.q.out): some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations, which may impact lineage; e.g. division (if it could "hide" a runtime division by zero) or operations involving cast (only "lossless" casts can be simplified), via: [CALCITE-7145] RexSimplify should not simplify IS NULL(10/0) (1.42) [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42) [CALCITE-7032] Simplify 'NULL>ALL (ARRAY[1,2,NULL])' to 'NULL' (1.41) Also, in lineage3.q.out, some condition re-ordering plus some new simplifications around IS_NOT_NULL: [CALCITE-5639] RexSimplify should remove IS NOT NULL check when LIKE comparison is present (1.35) (actually more generic than just LIKE) [CALCITE-5769] Optimizing 'CAST(e AS t) IS NOT NULL' to 'e IS NOT NULL' (1.35) (only "lossless" casts)
…alue BETWEEN 'val_103' AND 'val_105'" is pushed down and pre-computed before the join. The change comes from a new computation of Strong#isNull for SEARCH, introduced via [CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL' (1.40)
RexSimplify.simplifyCast DECIMAL casts are never removed, since they perform bounds checking, via [CALCITE-6685] Support checked arithmetic (1.39)
…NOT_NULL simplification adjustment: Before: - IF IsNotNull(cdouble) AND IsNotNull(log2(cint)) - THEN cdouble + log2(cint) - ELSE 0.0 After: - IF IsNotNull(cdouble + log2(cint)) - THEN cdouble + log2(cint) - ELSE 0.0 Due to some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations: [CALCITE-7145] RexSimplify should not simplify IS NULL(10/0) (1.42) [CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT)) (1.42)
str2 >= '1-2' AND str2 > '1-2' ==> str2 > '1-2' CAST(str1) IS NOT NULL AND (dt + CAST(str1)) = '2002-03-01' ==> (dt + CAST(str1)) = '2002-03-01' t <> '2002-04-01' AND t = '2002-03-01' ==> t = '2002-03-01' (dt + I) > T AND T < (dt + I) ==> T < (dt + I) Due to more aggressive simplifications: [CALCITE-4364] a IN (1,2) AND a = 1 should be simplified to a = 1 [CALCITE-5759] SEARCH(1, Sarg[IS NOT NULL]) should be simplified to TRUE [CALCITE-5780] Simplify '1 > x OR 1 <= x OR x IS NULL' to TRUE
…7.q:
Simplified (x >= 100 OR x <= 102) as a tautology on non-null x
Thus, residual filter predicates: {(((_col2 + _col5) >= 100.0D) or ((_col2 + _col5) <= 102.0D))} is removed, and instead we simply have IS_NOT_NULL(_col2) and IS_NOT_NULL(_col5)
[CALCITE-7194] "Simplify comparisons between function calls and literals to SEARCH" (1.41.0)
…t, vectorized_join46_mr.q.out, smb_mapjoin_46.q.out: Equivalent plan: expression "key BETWEEN 100 AND 102" is pushed down and pre-computed before the join. The change comes from a new computation of Strong#isNull for SEARCH, introduced via [CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL' (1.40) Test adjustment query53.q.out, query63.q.out Equivalent plan: conjunct reordering in AND of IN predicates SARG/predicate canonicalisation via [CALCITE-7194] "Simplify comparisons between function calls and literals to SEARCH" (1.41.0). Side effect: The row-count change is not caused by the predicate reordering itself (the filter is semantically identical). It comes from a different evaluation path that Hive's statistics estimator (HiveReduceExpressionsWithStatsRule / HiveReduceExpressionsRule) takes when the conjuncts are in a different order. Hive's statistics-based conjunct selectivity evaluation works by applying conjuncts left-to-right and cascadingly, this is inherently order-dependent when the column NDV/histogram data is imprecise.
Simplified predicate: if(decimal_col is not null, (CAST(decimal_col AS STRING) = '...'), false) AND date_col is not null AND decimal_col is not null => (CAST(decimal_col AS STRING) = '...') AND date_col is not null As a consequence, plan structure is modified due to statistics change: row count estimation has been reduced thanks to the predicate simplification. Finally: result order change. The plan restructuring changed which operator produces the final output rows and in what order, causing the two rows to swap: same output in different order; but since neither ORDER BY nor SORT_QUERY_RESULTS is used on the input file, the new result is also correct.
- materialized_view_rewrite_4.q.out Plan improvement. On test query "EXAMPLE 32" so far MV rewriting was NOT applied (full join+aggregate plan, Stage-1 Tez with 2 maps + 2 reducers), but now it is: direct scan of mv1_n3 (Stage-0 Fetch only) - materialized_view_partitioned_2.q REGRESSION, most efficient partition (partition_mv_3) is no longer used. Reason: mv "partition_mv_3 PARTITIONED ON (key)" is created as "SELECT value, key FROM src_txn_2 where key > 220 and key < 230" The regression queries are "... where key > 224 and key < 226;" and "... where key > 223 and key < 225;" so partition_mv_3 should be used. However, in Calcite's SubstitutionVisitor#splitFilter, instead of returning the appropriate filter, it now returns null. The cause is that this line "RexNode r = canonizeNode(rexBuilder, simplify.simplifyUnknownAsFalse(x2));" leads to an extra IS NOT NULL predicate on "r", which makes it not equivalent to "condition2" on the following line. The reason is an existing bug on a Calcite simplification: CALCITE-7635. This bug was so far "hidden", because this simplification was only applied for comparisons of the form "ref op literal", and in our query they are "literal op ref". But Calcite 1.35 introduced an improvement (CALCITE-5780) to support all comparisons formats, which enabled the problematic simplification that leads to the bug CALCITE-7635. Cleanup Adjust new test file (remove charset from string literals)
…rror: Caused by: java.lang.NoClassDefFoundError: org/joou/UShort at java.base/java.lang.Class.getDeclaredMethods0(Native Method) at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3580) at java.base/java.lang.Class.getMethodsRecursive(Class.java:3721) at java.base/java.lang.Class.getMethod0(Class.java:3707) at java.base/java.lang.Class.getMethod(Class.java:2395) at org.apache.calcite.linq4j.tree.Types.lookupMethod(Types.java:288) at org.apache.calcite.util.BuiltInMethod.<init>(BuiltInMethod.java:1043) at org.apache.calcite.util.BuiltInMethod.<clinit>(BuiltInMethod.java:320) at org.apache.calcite.rel.metadata.BuiltInMetadata$PercentageOriginalRows.<clinit>(BuiltInMetadata.java:396) ... This is required due to "[CALCITE-1466] Support for UNSIGNED types of TINYINT, SMALLINT, INT, BIGINT in type system" (1.41), which added this library in Calcite. Revert upgrade maven.shade.plugin.version in parent pom (not required for the ASM error) Review Adjust test iceberg_bucket_map_join_1.q.out
|
By chance I've stumbled upon the Bug class. It references workarounds for Calcite bugs. The Calcite upgrade fixes some of the bugs making the workarounds unnecessary. I suggest to open a follow-up ticket for removing the workarounds, to avoid making this big PR even bigger. |
Thanks for pointing this out. I have created a follow-up ticket HIVE-29734 for this purpose. |
|
I looked into the performance regression around partitioned materialized views (i.e., First of all, it appears the regression appears only in the context of MV rewrites so the impact is limited. Secondly, I assume that the performance/rewrite bug is already there even before the upgrade. If we modify the query to use For tracking purposes, please raise a JIRA ticket for this bug/regression and mark it as a blocker for the Hive 4.3.0 release adding the appropriate links with HIVE-29641 and potentially the next HIVE ticket tracking the upgrade to calcite 1.43.0. |
|
@zabetak thanks for the feedback; your analysis is correct. |
zabetak
left a comment
There was a problem hiding this comment.
I did a first pass covering mostly changes on .java and pom.xml. I will to do a pass over the .q.out files in the coming week.
| Statistics: Num rows: 2 Data size: 188 Basic stats: COMPLETE Column stats: COMPLETE | ||
| Filter Operator | ||
| predicate: ((UDFToDouble(hour) = 11.0D) and CAST( UDFToInteger((hr / 2.0D)) AS STRING) is not null) (type: boolean) | ||
| predicate: ((UDFToDouble(hour) = 11.0D) and hr is not null) (type: boolean) |
There was a problem hiding this comment.
The new plan is equivalent but it seems that the simplification didn't lead to a significant improvement. The simplification here seems to have introduced a new Filter[col0 is not null] higher up so at the end of the day the number of comparisons remains the same.
| public static void setCalciteSystemProperties() { | ||
| // turn off calcite rexnode normalization | ||
| System.setProperty("calcite.enable.rexnode.digest.normalize", "false"); | ||
| // update calcite default charset, consistent with HiveTypeFactory#getDefaultCharset | ||
| System.setProperty("calcite.default.charset", ConversionUtil.NATIVE_UTF16_CHARSET_NAME); | ||
| System.setProperty("calcite.default.nationalcharset", ConversionUtil.NATIVE_UTF16_CHARSET_NAME); | ||
| } | ||
|
|
There was a problem hiding this comment.
Setting calcite properties at this stage is very brittle. The CalciteSystemProperty class is initialized during class loading and remains immutable afterwards. This means that if during the JVM startup we load CalciteSystemProperty.class before Hive.class this settings will never take effect.
I know that this pattern was used before and it may have been my idea to do it as such in the first place but its definitely something that we should fix. I guess a better alternative would be to put these settings in saffron.properties file and package this inside the Hive jar (e.g., under src/main/resources).
This is not a blocker but something that we definitely need to follow-up if not treated here.
| aggCall.rexList, aggCall.getArgList(), aggCall.filterArg, aggCall.distinctKeys, aggCall.getCollation(), | ||
| aggCall.getType(), aggCall.getName()); |
There was a problem hiding this comment.
Logged https://issues.apache.org/jira/browse/CALCITE-7659 for having a more convenient way to do this copy.
| super(relBuilderFactory, generateUnionRewriting, unionRewritingPullProgram, fastBailOut); | ||
| } | ||
|
|
||
| // Overridden to avoid CALCITE-7641; TODO remove this once fixed. |
There was a problem hiding this comment.
nit: We can drop the TODO comments since we already have the Bug reference in the method. By doing this I guess we can get rid of some Sonar warnings.
| .withUnionRewritingPullProgram(PROGRAM) | ||
| .withRelBuilderFactory(HiveRelFactories.HIVE_BUILDER) | ||
| .toRule(); | ||
| new HiveMaterializedViewProjectFilterRule(HiveRelFactories.HIVE_BUILDER, true, PROGRAM, false); |
There was a problem hiding this comment.
nit: we could simplify the constructor to new HiveMaterializedViewProjectFilterRule() since we are always passing in the same param values.
| } | ||
| return null; | ||
| } | ||
|
|
There was a problem hiding this comment.
I am in favor of keeping the existing behavior via overriding for now. However, it makes me wonder when and why we are using the Calcite derivation logic. Hive has its own type derivation rules for division which are applied via org.apache.hadoop.hive.ql.udf.generic.GenericUDFOPDivide#deriveResultDecimalTypeInfo. I get the feeling that this method should use the logic in GenericUDFOPDivide. This is another important follow-up I guess.
| Hive.setCalciteSystemProperties(); | ||
|
|
There was a problem hiding this comment.
Too intrusive; let's find another way to set these.
| <!-- | ||
| Runtime dependencies required by org.apache.calcite.avatica:avatica-core:1.28.0 | ||
| (pulled in by Calcite 1.42+): Avatica now uses Apache HttpComponents 5 for its | ||
| HTTP/JDBC transport, so hive-exec must bundle httpcore5/httpclient5 or tests fail | ||
| with "NoClassDefFoundError: org/apache/hc/core5/util/Timeout". Avatica declares | ||
| these as <scope>runtime</scope> transitively, but they must be declared here | ||
| explicitly because Hive's enforcer bans org.apache.hc.core5/client5.** imports | ||
| elsewhere and the hive-exec shade plugin only bundles ql-level dependencies. | ||
| Can be removed once Hive migrates to HttpComponents 5 across the board. | ||
| --> | ||
| <dependency> | ||
| <groupId>org.apache.httpcomponents.core5</groupId> | ||
| <artifactId>httpcore5</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.apache.httpcomponents.client5</groupId> | ||
| <artifactId>httpclient5</artifactId> | ||
| </dependency> |
There was a problem hiding this comment.
First of all its unfortunate that we inherited yet another dependency from Avatica that we need to bundle in hive-exec but we don't have much of a choice right now. We could try to get rid of it in a subsequent Avatica release by repackaging the client differently.
Secondly, we may need to include the httpcore5 in the shaded jar otherwise queries may break if the dependencies are required at query runtime. Query execution happens in Tez and in a real cluster this has a different classpath than HS2. The shaded jar is part of the Tez classpath but not the HS2/ql dependencies. Tests cannot capture this kind of behavior so to test if everything works fine the easier would be to build the Hive docker image in the packaging module, run it, and test some DDL, DML, SQL, operations to see if everything works fine.
Thirdly, the statement that shade plugin bundles only ql-level dependencies is not true. You can pick which transitive dependencies are included by the appropriate include statements. The most vivid example is Avatica; it is not direct dependency of the ql module but it is included in the shaded jar.
Finally, if we end-up declaring httpcore5 explicitly here then probably we have to use runtime scope if we don't want to accidentally start using it the code.
| <!-- | ||
| Calcite is intentionally NOT included or relocated here. | ||
| Calcite's calcite-core ships org/apache/calcite/runtime/SqlFunctions.class with a | ||
| malformed RuntimeVisibleTypeAnnotations attribute (LOCAL_VARIABLE start_pc/length | ||
| pointing inside instruction operands in the static initializer). The JVM tolerates it; | ||
| ASM's ClassRemapper does not, and shading + javadoc fails with | ||
| "Index 65536 out of bounds for length 334 [...] Error in ASM processing class". | ||
| Present in Calcite 1.35.0 through at least 1.42.0. Upstream: ASM gitlab issue | ||
| https://gitlab.ow2.org/asm/asm/-/issues/318008 (closed as invalid: root cause is on | ||
| Calcite's side); Calcite ticket: https://issues.apache.org/jira/browse/CALCITE-6393 | ||
| (still unresolved). Druid 0.17.1 uses Calcite APIs compatible with Hive's Calcite 1.42+, | ||
| so skipping this relocation is safe. If Druid is upgraded to an incompatible Calcite, | ||
| restore the relocation (either after Calcite fixes their bytecode or by adding a shade | ||
| filter that excludes SqlFunctions.class from remapping). |
There was a problem hiding this comment.
Relocation is one thing and inclusion in the shaded jar is another. I suppose they included calcite in the shaded jar because the latter is send to workers (running in different JVMs) with a different classpath. Not sure if the Druid module can still work if you remove calcite from shading.
Druid 0.17.1 is using calcite 1.21.0. I highly doubt that all the used APIs are compatible with 1.42.0. In fact, I am rather skeptical about the compatibility with previous upgrades as well (1.25.0 and 1.33.0).
Out of curiosity, I tried running TestMiniDruidCliDriver today and realized that the Druid module is broken since 2021. I send also an email to https://lists.apache.org/thread/qr0xxkvvg4wrlz4kg0bmktldw96yvpn2 explaining the current status and favoring the complete removal of the module.
All in all, the changes here do not really matter and we don't have to sweat too much about it since it's only a matter of days before getting rid of the entire module.
| // Avoid creating incorrect expressions like $1 < NULL or $1 = NULL or NULL = NULL | ||
| // which may be problematic for Calcite later on | ||
| if (expr.isA(SqlKind.BINARY_COMPARISON) && expr.getKind() != SqlKind.IS_DISTINCT_FROM | ||
| && expr.getKind() != SqlKind.IS_NOT_DISTINCT_FROM) { | ||
| RexCall call = (RexCall) expr; | ||
| RexNode op0 = call.getOperands().get(0); | ||
| RexNode op1 = call.getOperands().get(1); | ||
| if ((op0.getKind() == SqlKind.LITERAL && ((RexLiteral) op0).isNull()) || | ||
| (op1.getKind() == SqlKind.LITERAL && ((RexLiteral) op1).isNull())) { | ||
| expr = rexBuilder.makeNullLiteral(expr.getType()); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Can we replace this code with a call to RexSimplify#simplifyComparisonWithNull?
The fact that we have to call simplification code here in order to avoid an AssertionError later on is worrisome. The choice to throw AssertionError from core calcite APIs was somewhat brutal. If we fail to handle "invalid" comparisons on some other part of the code we may end-up with irrecoverable errors at runtime. Anyways let's hope that its sufficient to update the code here and get done with it.
- refactor Calcite systems properties into saffron.properties file (remove them from Hive.java and TestHiveRelJsonReader.java) - minor changes in HiveMaterializedViewRule - use RexSimplify#simplifyComparisonWithNull in HiveFunctionHelper instead of ad-hoc code - minor improvements in HiveRelFieldTrimmer - minor improvements in hive-exec/pom.xml file
|



What changes were proposed in this pull request?
Upgrade Calcite to 1.42.0.
Why are the changes needed?
Upgrade to latest Calcite version.
Does this PR introduce any user-facing change?
Plan changes, see details below.
How was this patch tested?
Running all existing tests, making test adjustments where needed, see details below.
Details
pom changes
NoClassDefFoundError: org/apache/hc/core5/util/Timeoutwhen running certain tests (see more info on the comment of this file)NoClassDefFoundError: org/joou/UShort(seen on Jenkins init-metastore); this is due to "[CALCITE-1466] Support for UNSIGNED types of TINYINT, SMALLINT, INT, BIGINT in type system" (1.41)Error in ASM processing class org/apache/hive/druid/org/apache/calcite/runtime/SqlFunctions.class: Index 65536 out of bounds for length 334during shading / javadoc; the problem originates due to invalid byte code on Calcite: "[CALCITE-6393] Byte code of SqlFunctions is invalid" (already fixed on Calcite side for 1.43.0, so this workaround could be removed on a subsequent upgrade to said version).Code adjustments due to Calcite breaking changes
Use non-deprecated version of RexBuilder#makeAbstractCast in HiveSqlSumAggFunction, RexNodeConverter, ASTConverter
AggCall construction adjustments in HiveAggregate and HiveSqlSumAggFunction
Add visitLambda and visitLambdaRef in RexVisitor implementation (HiveCalciteUtil#ConstantFinder)
Add visit(LogicalRepeatUnion) and visit(LogicalAsofJoin) to HiveRelShuttleImpl
Adjust RexPermuteInputsShuttle constructor in HiveJoinConstraintsRule
Add visitNodeAndFieldIndex in RexVisitor implementation (HiveCalciteUtil#ConstantFinder)
Adjust HiveTypeSystemImpl
Other code changes
_UTF-16LE'1'instead of simply'1') due to the modification inSqlImplementor#toSql(RexLiteral)via "[CALCITE-6006] RelToSqlConverter loses charset information" (introduced in 1.36)As a consequence of defining Calcite default charset system property, now
RexLiteral#appendAsJava(which already contained the literal charset vs default charset verification, added to SqlImplementor in CALCITE-6006 1.36), literals printed via this method (i.e. used to print the charset) don't do it any more, e.g._UTF-16LE'ten'=>'ten', which aligns them with the ones printed viaSqlImplementor#toSql(RexLiteral)HiveCalciteUtil.stripHepVertices).This was also violated by materialization rules in HiveMaterializedViewRule, which used its own private static class HiveHepExtractRelNodeRule in a HepProgram as unionRewritingPullProgram. As a workaround, it is proposed to extend the MaterializedView rules (as HiveMaterializedViewRule), override the rewriteQuery
(where the UnionRewritingPullProgram is called), and unwrap the HepRelVertex tree ourselves (using
HiveCalciteUtil.stripHepVertices) before calling the super.rewriteQuery. This Calcite issue is being tracked via CALCITE-7641, (already fixed on Calcite side, so this workaround could be removed on a subsequent upgrade to Calcite 1.43.0).case CHAR_LENGTHin SqlFunctionConverter#buildAST, since a newSqlKind.CHAR_LENGTHhas been added (previously length functions hadSqlKind.OTHER_FUNCTION) via "[CALCITE-6182] Add LENGTH/LEN function (enabled in Snowflake library)" (in Calcite 1.37). Problem observed in several tests, wherelength(x)function not being evaluated (returning'length'literal instead of the actual value), e.g.TestJdbcDriver2#testExprCol:Illegal conversion to int from column 2 [length]HiveAggregate(group=[{0}], COUNT)into
HiveAggregate(group=[{}], COUNT)which is NOT the same semantics when the input is an empty Values (empty resultset vs 0). Issue seen on TestMiniLlapLocalCliDriver with empty_result.q which was incorrectly generating a Project+Agg+EmptyValues plan instead of simply EmptyValues before fixing HiveRelFieldTrimmer#generateNewGroupset:fieldsUsed.contains(aggregate.getGroupSet())but ratherfieldsUsed.intersects(aggregate.getGroupSet()).Also took the opportunity here to get rid of the unnecessary check aggregate.getIndicatorCount() > 0 which is always false (this method is deprecated and always returns zero)
java.lang.UnsupportedOperationException: Values with non-empty tuples are not supported. at org.apache.hadoop.hive.ql.optimizer.calcite.translator.ASTConverter.convert(ASTConverter.java:264). The reason is thatRelBuilder#project_contains the simplification (only applicable if config.simplifyValues is true): "If the expressions are all literals, and the input is a Values with N rows [...], replace with a Values with same tuple N times"; and with CALCITE-5717 that simplification was extended to not only "Values with N rows" but also "Aggregates with 1 row", and this case would lead to creating a non-empty HiveValues, which is not supported in our case. The easiest solution to prevent that from happening is disabling simplifyValues in HiveRelBuilder config.java.lang.AssertionError: Comparison with NULL in pulledUpPredicates, so in order to avoid that, a few classes needed to be adjusted. Firstly HiveFunctionHelper#getExpression must not create such expressions (seenNULL = NULLinTestMiniLlapLocalCliDriverwithsubquery_null_agg.q); secondly avoid incorrect comparison ($snapshotIdInputRef <= NULLseen inTestIcebergCliDriverwithmv_iceberg_orc.q) in HiveAugmentSnapshotMaterializationRule, use snapshotId -1 instead, and then switch it back in HivePushdownSnapshotFilterRule.Test plan adjustments
Perf Imp 1 ) materialized_view_rewrite_4.q.out
Plan improvement. On test query "EXAMPLE 32" MV rewriting was NOT applied so far (full join+aggregate plan, Stage-1 Tez with 2 maps + 2 reducers); but now it is: direct scan of mv1_n3 (Stage-0 Fetch only)
Perf Reg 1 ) materialized_view_partitioned_2.q
REGRESSION, most efficient alternative (partition_mv_3) is no longer used.
Reason: mv
partition_mv_3 PARTITIONED ON (key)is created asSELECT value, key FROM src_txn_2 where key > 220 and key < 230The regression queries are... where key > 224 and key < 226;and... where key > 223 and key < 225;sopartition_mv_3should be used.However, in Calcite's
SubstitutionVisitor#splitFilter, instead of returning the appropriate filter, it now returnsnull.The cause is that this line
RexNode r = canonizeNode(rexBuilder, simplify.simplifyUnknownAsFalse(x2));leads to an extraIS NOT NULLpredicate onr, which makes it not equivalent tocondition2on the following line.The reason is an existing bug on a Calcite simplification: CALCITE-7635. This bug was so far "hidden", because this simplification was only applied for comparisons of the form
ref op literal, and in our query they areliteral op ref. But Calcite 1.35 introduced an improvement (CALCITE-5780) to support all comparisons formats, which enabled the problematic simplification that leads to the bug CALCITE-7635. This bug has already been fixed on Calcite side, so this shall be corrected with the eventual upgrade to Calcite 1.43.0.0a ) Many plan changes of literals without charset, due to Calcite system property default charset definitions (see change on Hive.java above), after "CALCITE-6006: RelToSqlConverter loses charset information" (introduced in 1.36). Example:
Reason: "[CALCITE-5769] Optimizing 'CAST(e AS t) IS NOT NULL' to 'e IS NOT NULL'" (1.35) (only for "lossless" casts)
Reason: "[CALCITE-5639] RexSimplify should remove IS NOT NULL check when LIKE comparison is present" (actually more generic than just LIKE) (1.35)
or
Reason: Some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations, e.g. division (if it could "hide" a runtime division by zero) or operations involving cast (only "lossless" casts can be simplified)
"[CALCITE-7145] RexSimplify should not simplify IS NULL(10/0)" (1.42)
"[CALCITE-7296] RexSimplify should not simplify IS (NOT) NULL(CAST(10/0 AS BIGINT))" (1.42)
"[CALCITE-7295] RexSimplify should simplify a division with a NULL argument" (1.42) — added a new x/NULL → NULL rule, but only when the surrounding context is "safe".
"[CALCITE-7032] Simplify 'NULL>ALL (ARRAY[1,2,NULL])' to 'NULL'" (1.41)
Reason: "[CALCITE-5798] Improve simplification of '(x < y) IS NOT TRUE' when x and y are not nullable" (1.35)
Reason: "[CALCITE-4590] Incorrect query result with fixed-length string" (1.39)
NOTE: This fix only impacts predicates with OR/IN/SEARCH, and aligns these plans with single value condition ones (which already contained the padding, e.g. ql/src/test/results/clientpositive/perf/tpcds30tb/tez/cbo_query33.q.out)
Reason: "[CALCITE-6417] Map value constructor and Array value constructor unparsed incorrectly for HiveSqlDialect" (1.38)
Reason: "[CALCITE-5607] Serialize return type during RelJson.toJson(RexNode node) for SqlKind.MINUS" (1.37)
Reason: "[CALCITE-7160] Simplify AND/OR with DISTINCT predicates to SEARCH" (1.41)
Reason: "[CALCITE-6317] Incorrect constant replacement when group keys are NULL" (1.37)
Reason: "[CALCITE-7005] Invalid unparse for IS TRUE,IS FALSE,IS NOT TRUE and IS NOT FALSE in Hive/Presto Dialect" (1.40)
Reason: "[CALCITE-7194] Simplify comparisons between function calls and literals to SEARCH" (1.41)
The plan difference comes from:
It could be back to the original plan if we make SearchTransformer to detect and handle NOT BETWEEN (but that would impact other tests, so tbd separately)
Reason: "[CALCITE-7083] RelMdDistinctRowCount aggregates implementation problems" (1.41)
Reason: "[CALCITE-6748] RelToSqlConverter returns the wrong result when Aggregate is on Sort" (1.39) (and also "[CALCITE-6417] Map value constructor and Array value constructor unparsed incorrectly for HiveSqlDialect" (1.38))
Reason: condition in different order (see 11), which leads to numRows diff, which in turn leads to minReductionHashAggr diff; all comes from StatsRulesProcFactory.java:367-391 treats AND non-commutatively, order matters due to iteratively rounding
Reason: "[CALCITE-5677] SUBSTR signature incorrect for BigQuery" (1.35)
The new emitted SQL is valid in every dialect that Hive is realistically going to push down to (Derby, MySQL, PostgreSQL, MS SQL, Oracle, BigQuery all accept comma-separated SUBSTRING/SUBSTR).
It's actually more dialect-portable than the old form: the old SUBSTRING(x FROM y FOR z) is SQL-92/99 syntax that some older or stricter dialects don't accept, while comma-form is universal. CALCITE-5677 was made specifically to fix the BigQuery breakage.
Reason: "[CALCITE-4334] LITERAL_AGG, an aggregate function that returns a constant value" (1.35)
18 ) cross_prod_1.q.out
Equivalent plan: expression "A.value BETWEEN 'val_103' AND 'val_105'" is pushed down and pre-computed before the join. The change comes from a new computation of Strong#isNull for SEARCH, introduced via
"[CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL'" (1.40)
19 ) join46.q.out, mapjoin46.q.out, vectorized_join46.q.out, vectorized_join46_mr.q.out, smb_mapjoin_46.q.out:
Equivalent plan: expression "key BETWEEN 100 AND 102" is pushed down and pre-computed before the join. The change comes from a new computation of Strong#isNull for SEARCH, introduced via
"[CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL'" (1.40) (Same as previous point)
20 ) vector_aggregate_9.q.out
RexSimplify.simplifyCast DECIMAL casts are never removed, since they perform bounds checking, via
"[CALCITE-6685] Support checked arithmetic" (1.39)
21 ) vector_coalesce.q.out: equivalent plan, with some IS_NOT_NULL simplification adjustment:
Before:
After:
Due to some IS [NOT] NULL simplifications have been removed from RexSimplify for "non-safe" operations (see 3).
Due to more aggressive simplifications:
"[CALCITE-4364] a IN (1,2) AND a = 1 should be simplified to a = 1"
"[CALCITE-5759] SEARCH(1, Sarg[IS NOT NULL]) should be simplified to TRUE"
"[CALCITE-5780] Simplify '1 > x OR 1 <= x OR x IS NULL' to TRUE"
23 ) join45.q.out, join47.q.out, mapjoin47.q, smb_mapjoin_47.q
Simplified: (x >= 100 OR x <= 102) as a tautology on non-null x
Thus, residual filter predicates: {(((_col2 + _col5) >= 100.0D) or ((_col2 + _col5) <= 102.0D))} is removed, and instead we simply have IS_NOT_NULL(_col2) and IS_NOT_NULL(_col5)
Thanks to
"[CALCITE-7194] Simplify comparisons between function calls and literals to SEARCH" (1.41.0)
24 ) query53.q.out, query63.q.out
Equivalent plan: conjunct reordering in AND of IN predicates
SARG/predicate canonicalisation via "[CALCITE-7194] Simplify comparisons between function calls and literals to SEARCH" (1.41.0).
Side effect: The row-count change is not caused by the predicate reordering itself (the filter is semantically identical). It comes from a different evaluation path that Hive's statistics estimator (HiveReduceExpressionsWithStatsRule / HiveReduceExpressionsRule) takes when the conjuncts are in a different order. Hive's statistics-based conjunct selectivity evaluation works by applying conjuncts left-to-right and cascadingly, this is inherently order-dependent when the column NDV/histogram data is imprecise (see 15).
25 ) iceberg_bucket_map_join_1.q.out
Simplified predicate (primary difference):
Secondary changes derived from the simpler predicate:
All row-count estimates collapse to rows=1 (was: target_table TS=20/Filter=10, source_table TS=7/Filter=3); target_table Filter width 260→252.
The two queries with
SELECT DISTINCTin the subquery (4 of the 8 explains, across bucket-map-join × vectorized configs) restructure their vertex dependency:Map 1 <- Reducer 3becomesReducer 3 <- Map 1, Map 2. With row estimates that small, target_table is shuffled/broadcast into Reducer 3 where the join now happens. The non-DISTINCT queries keep their original shape.Grouping Num Buckets:7, Grouping Partition Columnsannotation drops from target_table's TS on the 4 restructured plans (metadata surface only). Result rows2022-08-16and2022-08-30swap order in 4 places — same result set, different emission order (final projection now in Reducer 3).