PaulJackson123 commented on code in PR #3495:
URL: https://github.com/apache/calcite/pull/3495#discussion_r1406903023
##########
core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java:
##########
@@ -526,12 +525,78 @@ private static ImmutableBitSet
decorateWithConstantColumnsFromPredicates(
ImmutableBitSet checkingColumns, RelNode rel, RelMetadataQuery mq) {
final RelOptPredicateList predicates = mq.getPulledUpPredicates(rel);
if (!RelOptPredicateList.isEmpty(predicates)) {
- ImmutableBitSet invariantIndexes = predicates.getInvariantColumnSet();
- if (!invariantIndexes.isEmpty()) {
- return checkingColumns.union(ImmutableBitSet.of(invariantIndexes));
+ ImmutableBitSet constantIndexes = getConstantColumnSet(predicates);
+ if (!constantIndexes.isEmpty()) {
+ return checkingColumns.union(ImmutableBitSet.of(constantIndexes));
}
}
- // If no invariant columns deduced, return the original "checkingColumns".
+ // If no constant columns deduced, return the original "checkingColumns".
return checkingColumns;
}
+
+ /**
+ * Return the set of columns that are set to a constant literal or a scalar
query (as
+ * in a correlated subquery). Examples of constants are {@code x} in the
following:
+ * <pre>SELECT x FROM table WHERE x = 5</pre>
+ * and
+ * <pre>SELECT x, y FROM table WHERE x = (SELECT MAX(x) FROM table)</pre>
+ * <p/>
+ * NOTE: Subqueries that reference correlating variables are not considered
constant:
+ * <pre>SELECT x, y FROM table A WHERE x = (SELECT MAX(x) FROM table B WHERE
A.y = B.y)</pre>
+ */
+ static ImmutableBitSet getConstantColumnSet(RelOptPredicateList
relOptPredicateList) {
+ ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
+ relOptPredicateList.constantMap.keySet()
+ .stream()
+ .filter(RexInputRef.class::isInstance)
+ .map(RexInputRef.class::cast)
+ .map(RexSlot::getIndex)
+ .forEach(builder::set);
+
+ relOptPredicateList.pulledUpPredicates.forEach(rex -> {
+ if (rex.getKind() == SqlKind.EQUALS
+ || rex.getKind() == SqlKind.IS_NOT_DISTINCT_FROM) {
+ List<RexNode> ops = ((RexCall) rex).getOperands();
+ RexNode op0 = ops.get(0);
+ RexNode op1 = ops.get(1);
+ addInputRefIfOtherConstant(builder, op0, op1);
+ addInputRefIfOtherConstant(builder, op1, op0);
+ }
+ });
+
+ return builder.build();
+ }
+
+ private static void addInputRefIfOtherConstant(ImmutableBitSet.Builder
builder, RexNode inputRef,
+ RexNode other) {
+ if (inputRef instanceof RexInputRef
+ && (other.getKind() == SqlKind.LITERAL ||
isConstantScalarQuery(other))) {
+ builder.set(((RexInputRef) inputRef).getIndex());
+ }
+ }
+
+ /**
+ * Returns whether the supplied {@link RexNode} is a constant scalar
subquery - one that does not
+ * reference any correlating variables.
+ */
+ private static boolean isConstantScalarQuery(RexNode rexNode) {
+ if (rexNode.getKind() == SqlKind.SCALAR_QUERY) {
+ MutableBoolean hasCorrelatedVars = new MutableBoolean(false);
+ ((RexSubQuery) rexNode).rel.accept(new RelShuttleImpl() {
+ @Override public RelNode visit(final LogicalFilter filter) {
+ filter.getCondition().accept(new RexShuttle() {
+ @Override public RexNode visitFieldAccess(final RexFieldAccess
fieldAccess) {
+ if (fieldAccess.getReferenceExpr().getKind() ==
SqlKind.CORREL_VARIABLE) {
+ hasCorrelatedVars.setTrue();
+ }
+ return super.visitFieldAccess(fieldAccess);
+ }
+ });
+ return super.visit(filter);
+ }
Review Comment:
This change was made to account to the fact that scalar subqueries are not
constant when they have correlating variables. Not sure this is the best way to
detect correlating variables. Also, only checking filters here - not sure what
to do about Calc or others.
--
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]