zabetak commented on code in PR #6523:
URL: https://github.com/apache/hive/pull/6523#discussion_r3595228728
##########
iceberg/iceberg-handler/src/test/results/positive/dynamic_partition_pruning.q.out:
##########
@@ -1458,37 +1458,40 @@ STAGE PLANS:
Map Operator Tree:
TableScan
alias: srcpart_double_hour_n0
- filterExpr: ((UDFToDouble(hour) = 11.0D) and CAST(
UDFToInteger((hr / 2.0D)) AS STRING) is not null) (type: boolean)
+ filterExpr: ((UDFToDouble(hour) = 11.0D) and hr is not null)
(type: boolean)
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)
Review 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.
##########
ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestHiveRelJsonReader.java:
##########
@@ -57,6 +58,8 @@ static Stream<Path> inputJsonFiles() throws IOException {
@ParameterizedTest
@MethodSource("inputJsonFiles")
void testReadJson(Path jsonFile) throws IOException {
+ Hive.setCalciteSystemProperties();
+
Review Comment:
Too intrusive; let's find another way to set these.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/views/HiveMaterializedViewRule.java:
##########
@@ -145,31 +153,142 @@ public HiveMaterializedViewOnlyAggregateRule(
super(relBuilderFactory, generateUnionRewriting,
unionRewritingPullProgram);
}
+ // Overridden to avoid CALCITE-7641; TODO remove this once fixed.
+ @Override protected @Nullable RelNode rewriteQuery(
+ RelBuilder relBuilder,
+ RexBuilder rexBuilder,
+ RexSimplify simplify,
+ RelMetadataQuery mq,
+ RexNode compensationColumnsEquiPred,
+ RexNode otherCompensationPred,
+ @Nullable Project topProject,
+ RelNode node,
+ BiMap<RexTableInputRef.RelTableRef, RexTableInputRef.RelTableRef>
queryToViewTableMapping,
+ EquivalenceClasses viewEC, EquivalenceClasses queryEC) {
+ if (Bug.CALCITE_7641_FIXED) {
+ throw new AssertionError("Overriding should be removed");
+ }
+ RelNode unwrappedNode = HiveCalciteUtil.stripHepVertices(node);
+ return super.rewriteQuery(relBuilder, rexBuilder, simplify, mq,
compensationColumnsEquiPred,
+ otherCompensationPred, topProject, unwrappedNode,
queryToViewTableMapping, viewEC, queryEC);
+ }
+
@Override
protected SqlFunction getFloorSqlFunction(TimeUnitRange flag) {
return HiveRelBuilder.getFloorSqlFunction(flag);
}
}
- /**
- * This rule is used within the PROGRAM that rewrites the query for
- * partial rewritings. Its goal is to extract the RelNode from the
- * HepRelVertex node so the rest of the rules in the PROGRAM can be
- * applied correctly.
- */
- private static class HiveHepExtractRelNodeRule extends RelOptRule {
+ protected static class HiveMaterializedViewProjectFilterRule extends
MaterializedViewProjectFilterRule {
+ public HiveMaterializedViewProjectFilterRule(RelBuilderFactory
relBuilderFactory,
+ boolean generateUnionRewriting, HepProgram unionRewritingPullProgram,
+ boolean fastBailOut) {
+ super(relBuilderFactory, generateUnionRewriting,
unionRewritingPullProgram, fastBailOut);
+ }
+
+ // Overridden to avoid CALCITE-7641; TODO remove this once fixed.
+ @Override protected @Nullable RelNode rewriteQuery(
+ RelBuilder relBuilder,
+ RexBuilder rexBuilder,
+ RexSimplify simplify,
+ RelMetadataQuery mq,
+ RexNode compensationColumnsEquiPred,
+ RexNode otherCompensationPred,
+ @Nullable Project topProject,
+ RelNode node,
+ BiMap<RexTableInputRef.RelTableRef, RexTableInputRef.RelTableRef>
viewToQueryTableMapping,
+ EquivalenceClasses viewEC, EquivalenceClasses queryEC) {
+ if (Bug.CALCITE_7641_FIXED) {
+ throw new AssertionError("Overriding should be removed");
+ }
+ RelNode unwrappedNode = HiveCalciteUtil.stripHepVertices(node);
+ return super.rewriteQuery(relBuilder, rexBuilder, simplify, mq,
compensationColumnsEquiPred,
+ otherCompensationPred, topProject, unwrappedNode,
viewToQueryTableMapping, viewEC, queryEC);
+ }
+ }
+
+ protected static class HiveMaterializedViewOnlyFilterRule extends
MaterializedViewOnlyFilterRule {
+ public HiveMaterializedViewOnlyFilterRule(RelBuilderFactory
relBuilderFactory,
+ boolean generateUnionRewriting, HepProgram unionRewritingPullProgram,
+ boolean fastBailOut) {
+ super(relBuilderFactory, generateUnionRewriting,
unionRewritingPullProgram, fastBailOut);
+ }
- private static final HiveHepExtractRelNodeRule INSTANCE =
- new HiveHepExtractRelNodeRule();
+ // Overridden to avoid CALCITE-7641; TODO remove this once fixed.
+ @Override protected @Nullable RelNode rewriteQuery(
+ RelBuilder relBuilder,
+ RexBuilder rexBuilder,
+ RexSimplify simplify,
+ RelMetadataQuery mq,
+ RexNode compensationColumnsEquiPred,
+ RexNode otherCompensationPred,
+ @Nullable Project topProject,
+ RelNode node,
+ BiMap<RexTableInputRef.RelTableRef, RexTableInputRef.RelTableRef>
viewToQueryTableMapping,
+ EquivalenceClasses viewEC, EquivalenceClasses queryEC) {
+ if (Bug.CALCITE_7641_FIXED) {
+ throw new AssertionError("Overriding should be removed");
+ }
+ RelNode unwrappedNode = HiveCalciteUtil.stripHepVertices(node);
+ return super.rewriteQuery(relBuilder, rexBuilder, simplify, mq,
compensationColumnsEquiPred,
+ otherCompensationPred, topProject, unwrappedNode,
viewToQueryTableMapping, viewEC, queryEC);
+ }
+ }
- private HiveHepExtractRelNodeRule() {
- super(operand(HepRelVertex.class, any()));
+ protected static class HiveMaterializedViewProjectJoinRule extends
MaterializedViewProjectJoinRule {
+ public HiveMaterializedViewProjectJoinRule(RelBuilderFactory
relBuilderFactory,
+ boolean generateUnionRewriting, HepProgram unionRewritingPullProgram,
+ boolean fastBailOut) {
+ super(relBuilderFactory, generateUnionRewriting,
unionRewritingPullProgram, fastBailOut);
}
- @Override
- public void onMatch(RelOptRuleCall call) {
- final HepRelVertex rel = call.rel(0);
- call.transformTo(rel.getCurrentRel());
+ // Overridden to avoid CALCITE-7641; TODO remove this once fixed.
+ @Override protected @Nullable RelNode rewriteQuery(
+ RelBuilder relBuilder,
+ RexBuilder rexBuilder,
+ RexSimplify simplify,
+ RelMetadataQuery mq,
+ RexNode compensationColumnsEquiPred,
+ RexNode otherCompensationPred,
+ @Nullable Project topProject,
+ RelNode node,
+ BiMap<RexTableInputRef.RelTableRef, RexTableInputRef.RelTableRef>
viewToQueryTableMapping,
+ EquivalenceClasses viewEC, EquivalenceClasses queryEC) {
+ if (Bug.CALCITE_7641_FIXED) {
+ throw new AssertionError("Overriding should be removed");
+ }
+ RelNode unwrappedNode = HiveCalciteUtil.stripHepVertices(node);
+ return super.rewriteQuery(relBuilder, rexBuilder, simplify, mq,
compensationColumnsEquiPred,
+ otherCompensationPred, topProject, unwrappedNode,
viewToQueryTableMapping, viewEC, queryEC);
+ }
+ }
+
+ protected static class HiveMaterializedViewOnlyJoinRule extends
MaterializedViewOnlyJoinRule {
+ public HiveMaterializedViewOnlyJoinRule(RelBuilderFactory
relBuilderFactory,
+ boolean generateUnionRewriting, HepProgram unionRewritingPullProgram,
+ boolean fastBailOut) {
+ super(relBuilderFactory, generateUnionRewriting,
unionRewritingPullProgram, fastBailOut);
+ }
+
+ // Overridden to avoid CALCITE-7641; TODO remove this once fixed.
Review 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.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/views/HiveMaterializedViewRule.java:
##########
@@ -78,36 +86,16 @@ public class HiveMaterializedViewRule {
.build();
public static final MaterializedViewProjectFilterRule
INSTANCE_PROJECT_FILTER =
- (MaterializedViewProjectFilterRule)
MaterializedViewProjectFilterRule.Config.DEFAULT
- .withGenerateUnionRewriting(true)
- .withFastBailOut(false)
- .withUnionRewritingPullProgram(PROGRAM)
- .withRelBuilderFactory(HiveRelFactories.HIVE_BUILDER)
- .toRule();
+ new HiveMaterializedViewProjectFilterRule(HiveRelFactories.HIVE_BUILDER,
true, PROGRAM, false);
Review Comment:
nit: we could simplify the constructor to `new
HiveMaterializedViewProjectFilterRule()` since we are always passing in the
same param values.
##########
druid-handler/pom.xml:
##########
@@ -353,10 +353,24 @@
<pattern>io.netty</pattern>
<shadedPattern>org.apache.hive.druid.io.netty</shadedPattern>
</relocation>
+ <!--
+ 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).
Review 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.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveTypeSystemImpl.java:
##########
@@ -189,4 +181,39 @@ public RelDataType deriveSumType(RelDataTypeFactory
typeFactory,
return argumentType;
}
+ /**
+ * Overridden because CALCITE-6464 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.
+ */
+ @Override
+ public RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory,
+ RelDataType type1, RelDataType type2) {
+ if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)
&&
+ (SqlTypeUtil.isDecimal(type1) || SqlTypeUtil.isDecimal(type2))) {
+ // Java numeric will always have invalid precision/scale,
+ // use its default decimal precision/scale instead.
+ type1 = RelDataTypeFactoryImpl.isJavaType(type1) ?
typeFactory.decimalOf(type1) : type1;
+ type2 = RelDataTypeFactoryImpl.isJavaType(type2) ?
typeFactory.decimalOf(type2) : type2;
+ int p1 = type1.getPrecision();
+ int p2 = type2.getPrecision();
+ int s1 = type1.getScale();
+ int s2 = type2.getScale();
+
+ final int maxNumericPrecision = getMaxNumericPrecision();
+ int dout = Math.min(p1 - s1 + s2, maxNumericPrecision);
+ int scale = Math.max(6, s1 + p2 + 1);
+ scale = Math.min(scale, maxNumericPrecision - dout);
+ scale = Math.min(scale, getMaxNumericScale());
+
+ int precision = dout + scale;
+ assert precision <= maxNumericPrecision;
+ assert precision > 0;
+ return typeFactory.createSqlType(SqlTypeName.DECIMAL, precision, scale);
+ }
+ return null;
+ }
+
Review 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.
##########
ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java:
##########
@@ -559,6 +560,14 @@ public static void closeCurrent() {
hiveDB.remove();
}
+ 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);
+ }
+
Review 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.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/reloperators/HiveAggregate.java:
##########
@@ -75,8 +75,8 @@ private static AggregateCall replaceAggFunction(RelDataType
rowType, AggregateCa
SqlTypeUtil.projectTypes(rowType, aggCall.getArgList()),
aggCall.getType());
return AggregateCall.create(aggFunction, aggCall.isDistinct(),
aggCall.isApproximate(), aggCall.ignoreNulls(),
- aggCall.getArgList(), aggCall.filterArg, aggCall.distinctKeys,
aggCall.getCollation(), aggCall.getType(),
- aggCall.getName());
+ aggCall.rexList, aggCall.getArgList(), aggCall.filterArg,
aggCall.distinctKeys, aggCall.getCollation(),
+ aggCall.getType(), aggCall.getName());
Review Comment:
Logged https://issues.apache.org/jira/browse/CALCITE-7659 for having a more
convenient way to do this copy.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRelFieldTrimmer.java:
##########
@@ -517,7 +517,8 @@ private ImmutableBitSet generateNewGroupset(Aggregate
aggregate, ImmutableBitSet
RelMetadataQuery mq = aggregate.getCluster().getMetadataQuery();
final Set<ImmutableBitSet> uniqueKeys = mq.getUniqueKeys(input, false);
- if (uniqueKeys == null || uniqueKeys.isEmpty()) {
+ if (uniqueKeys == null || uniqueKeys.isEmpty() ||
+ (uniqueKeys.size() == 1 && uniqueKeys.iterator().next().isEmpty())) {
Review Comment:
If uniqueKeys is the empty set (`{}`) then all columns are considered unique
according to the Javadoc. I think we can avoid calling
`generateGroupSetIfCardinalitySame` which can be expensive (since it involves
metadata calls to lineage) and handle the case either in the for-loop below
either in a separate if-block.
Basically when unique keys is the empty set I guess we can drop all unused
columns as far as we keep at least one in place to avoid altering the group by
semantics and hitting the bug you outlined in `empty_result.q` file.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRelFieldTrimmer.java:
##########
@@ -549,9 +550,8 @@ private ImmutableBitSet generateNewGroupset(Aggregate
aggregate, ImmutableBitSet
*/
private Aggregate rewriteGBConstantKeys(Aggregate aggregate, ImmutableBitSet
fieldsUsed,
ImmutableBitSet aggCallFields) {
- if ((aggregate.getIndicatorCount() > 0)
- || (aggregate.getGroupSet().isEmpty())
- || fieldsUsed.contains(aggregate.getGroupSet())) {
+ if (aggregate.getGroupSet().isEmpty()
+ || fieldsUsed.intersects(aggregate.getGroupSet())) {
Review Comment:
Indeed the new condition seems more aligned with the Javadoc. However, it
seems that there is at least one more bug in this method since I don't see any
handling of grouping sets. It's not related to the upgrade so let's create a
follow-up to fix it later.
##########
ql/src/java/org/apache/hadoop/hive/ql/parse/type/HiveFunctionHelper.java:
##########
@@ -336,6 +336,19 @@ public RexNode getExpression(String functionText,
FunctionInfo fi,
RexUtil.flatten(call.getOperands(), call.getOperator()));
}
+ // 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());
+ }
+ }
+
Review 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.
##########
ql/pom.xml:
##########
@@ -365,6 +365,24 @@
<artifactId>hadoop-yarn-client</artifactId>
<optional>true</optional>
</dependency>
+ <!--
+ 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>
Review 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.
##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRelFieldTrimmer.java:
##########
@@ -517,7 +517,8 @@ private ImmutableBitSet generateNewGroupset(Aggregate
aggregate, ImmutableBitSet
RelMetadataQuery mq = aggregate.getCluster().getMetadataQuery();
final Set<ImmutableBitSet> uniqueKeys = mq.getUniqueKeys(input, false);
- if (uniqueKeys == null || uniqueKeys.isEmpty()) {
+ if (uniqueKeys == null || uniqueKeys.isEmpty() ||
+ (uniqueKeys.size() == 1 && uniqueKeys.iterator().next().isEmpty())) {
Review Comment:
Apart from that it's worth double-checking other calls of `getUniqueKeys` in
Hive code base (e.g., `HiveAggregateJoinTransposeRule`) to ensure that we are
not going to introduce wrong results due to the new behavior.
--
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]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]