mihaibudiu commented on code in PR #5235:
URL: https://github.com/apache/calcite/pull/5235#discussion_r3916685285
##########
core/src/main/java/org/apache/calcite/rel/metadata/RelMdFunctionalDependency.java:
##########
@@ -428,50 +466,93 @@ private ArrowSet getCalcFD(Calc rel, RelMetadataQuery mq)
{
}
/**
- * Shifts column indices in functional dependencies (for right table in
Joins).
- *
- * @param fdSet Functional dependency set
- * @param offset Index offset
- * @return Shifted functional dependency set
+ * Adds functional dependencies implied by a join condition.
*/
- private ArrowSet shiftFdSet(ArrowSet fdSet, int offset) {
- ArrowSet.Builder shiftedFdSetBuilder = new ArrowSet.Builder();
- for (Arrow fd : fdSet.getArrows()) {
- ImmutableBitSet shiftedDeterminants = fd.getDeterminants().shift(offset);
- ImmutableBitSet shiftedDependents = fd.getDependents().shift(offset);
- shiftedFdSetBuilder.addArrow(shiftedDeterminants, shiftedDependents);
+ private static void addFDsFromJoinCondition(Join rel, int leftFieldCount,
+ ArrowSet.Builder builder) {
+ final JoinRelType joinType = rel.getJoinType();
+ if (joinType == JoinRelType.INNER) {
+ addBidirectionalFDsFromEqualityCondition(rel.getCondition(), builder);
+ return;
}
- return shiftedFdSetBuilder.build();
+
+ if (joinType == JoinRelType.LEFT || joinType == JoinRelType.RIGHT) {
+ final JoinInfo joinInfo = rel.analyzeCondition();
+ if (!joinInfo.isEqui() || joinInfo.leftKeys.isEmpty()
+ || !fieldsSupportEqualityInference(rel.getLeft(), joinInfo.leftSet())
+ || !fieldsSupportEqualityInference(rel.getRight(),
joinInfo.rightSet())) {
+ return;
+ }
+
+ final ImmutableBitSet leftKeys = joinInfo.leftSet();
+ final ImmutableBitSet rightKeys =
joinInfo.rightSet().shift(leftFieldCount);
+ if (joinType == JoinRelType.LEFT) {
+ builder.addArrow(leftKeys, rightKeys);
+ } else {
+ builder.addArrow(rightKeys, leftKeys);
+ }
+ return;
+ }
+
+ throw new AssertionError("unsupported join type: " + joinType);
}
/**
- * Extracts functional dependencies from equality and AND conditions.
- * Handles col1 = col2, col1 IS NOT DISTINCT FROM col2, and AND conditions.
+ * Adds bidirectional dependencies for input-reference equalities in a
+ * condition. Callers are responsible for ensuring that every output row
+ * satisfies the condition, as is true for Filters and inner joins.
*/
- private static void addFDsFromEqualityCondition(RexNode condition,
ArrowSet.Builder builder) {
- if (!(condition instanceof RexCall)) {
- return;
- }
+ private static void addBidirectionalFDsFromEqualityCondition(
+ RexNode condition, ArrowSet.Builder builder) {
+ for (RexNode conjunct : RelOptUtil.conjunctions(condition)) {
+ if (!(conjunct instanceof RexCall)) {
+ continue;
+ }
- RexCall call = (RexCall) condition;
- if (call.getOperator().getKind() == SqlKind.EQUALS
- || call.getOperator().getKind() == SqlKind.IS_NOT_DISTINCT_FROM) {
+ RexCall call = (RexCall) conjunct;
+ if (call.getOperator().getKind() != SqlKind.EQUALS
+ && call.getOperator().getKind() != SqlKind.IS_NOT_DISTINCT_FROM) {
+ continue;
+ }
List<RexNode> operands = call.getOperands();
if (operands.size() == 2) {
RexNode left = operands.get(0);
RexNode right = operands.get(1);
- if (left instanceof RexInputRef && right instanceof RexInputRef) {
+ if (left instanceof RexInputRef && right instanceof RexInputRef
+ && typeSupportsGroupKeyInference(left.getType())
+ && typeSupportsGroupKeyInference(right.getType())) {
int leftRef = ((RexInputRef) left).getIndex();
int rightRef = ((RexInputRef) right).getIndex();
builder.addBidirectionalArrow(leftRef, rightRef);
}
}
- } else if (call.getOperator().getKind() == SqlKind.AND) {
- for (RexNode operand : call.getOperands()) {
- addFDsFromEqualityCondition(operand, builder);
+ }
+ }
+
+ /**
+ * Returns whether equality on the given fields can safely imply a functional
+ * dependency for grouping purposes.
+ */
+ private static boolean fieldsSupportEqualityInference(RelNode input,
+ ImmutableBitSet fields) {
+ for (int field : fields) {
+ if (!typeSupportsGroupKeyInference(
+ input.getRowType().getFieldList().get(field).getType())) {
+ return false;
}
}
+ return true;
+ }
+
+ /**
+ * Returns whether a type can safely be used to infer that one grouping key
+ * determines another. Approximate numerics and intervals are unsafe,
+ * including when nested in rows, collections, or maps.
+ */
+ private static boolean typeSupportsGroupKeyInference(RelDataType type) {
Review Comment:
why are intervals unsafe?
--
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]