fresh-borzoni commented on code in PR #29653:
URL: https://github.com/apache/flink/pull/29653#discussion_r4526613106


##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/UpsertKeyUtil.java:
##########
@@ -63,4 +78,97 @@ public static Optional<int[]> smallestKey(@Nullable 
Set<ImmutableBitSet> upsertK
                 
upsertKeys.stream().map(ImmutableBitSet::toArray).collect(Collectors.toList());
         return Optional.of(UpsertKeyUtils.smallestKey(asArrays));
     }
+
+    /**
+     * Returns the upsert keys of the given node, plus each key whose columns 
outside {@code
+     * columns} can all be replaced by columns inside that are equal to them 
on every row, e.g.
+     * through an inner equi-join condition. For example, for {@code t1 JOIN 
t2 ON a1 = a2} and
+     * columns {a1, b2}, the upsert key {a2, b2} adds {a1, b2}.
+     *
+     * @return the upsert keys and the aligned ones, or null if the upsert 
keys are unknown
+     */
+    public static @Nullable Set<ImmutableBitSet> getUpsertKeysAlignedTo(
+            RelNode rel, ImmutableBitSet columns, RelMetadataQuery mq) {
+        final FlinkRelMetadataQuery fmq = 
FlinkRelMetadataQuery.reuseOrCreate(mq);
+        final Set<ImmutableBitSet> upsertKeys = fmq.getUpsertKeys(rel);
+        if (upsertKeys == null || 
upsertKeys.stream().allMatch(columns::contains)) {
+            return upsertKeys;
+        }
+
+        final Map<Integer, Integer> parents = getEqualColumnParents(rel, fmq);
+        final Map<Integer, Integer> replacements = new HashMap<>();
+        for (int column : columns) {
+            replacements.putIfAbsent(root(parents, column), column);
+        }
+        final Set<ImmutableBitSet> alignedKeys = new HashSet<>(upsertKeys);
+        for (ImmutableBitSet key : upsertKeys) {
+            final ImmutableBitSet.Builder alignedKey = 
ImmutableBitSet.builder();
+            for (int column : key) {
+                alignedKey.set(
+                        columns.get(column)
+                                ? column
+                                : replacements.getOrDefault(root(parents, 
column), column));
+            }
+            final ImmutableBitSet aligned = alignedKey.build();
+            if (columns.contains(aligned)) {
+                alignedKeys.add(aligned);
+            }
+        }
+        return alignedKeys;
+    }
+
+    /**
+     * Returns {@code columns} and every column of the given node that is 
equal to one of them on
+     * every row.
+     */
+    public static ImmutableBitSet getEqualColumns(
+            RelNode rel, ImmutableBitSet columns, RelMetadataQuery mq) {
+        final Map<Integer, Integer> parents = getEqualColumnParents(rel, mq);
+        final Set<Integer> roots =
+                columns.asList().stream()
+                        .map(column -> root(parents, column))
+                        .collect(Collectors.toSet());
+        final ImmutableBitSet.Builder equalColumns = columns.rebuild();
+        for (int column : parents.keySet()) {
+            final int root = root(parents, column);
+            if (roots.contains(root)) {
+                equalColumns.set(column).set(root);
+            }
+        }
+        return equalColumns.build();
+    }
+
+    /** Groups the columns that are equal on every row, as a union-find forest 
of parents. */
+    private static Map<Integer, Integer> getEqualColumnParents(RelNode rel, 
RelMetadataQuery mq) {
+        final Map<Integer, Integer> parents = new HashMap<>();
+        for (RexNode predicate : 
mq.getPulledUpPredicates(rel).pulledUpPredicates) {
+            if (!predicate.isA(EQUALITY_KINDS)) {
+                continue;
+            }
+            final RexNode left = ((RexCall) predicate).getOperands().get(0);
+            final RexNode right = ((RexCall) predicate).getOperands().get(1);
+            // comparing different types may implicitly cast one side, which 
needn't be injective,
+            // and FLOAT/DOUBLE compare -0.0 = 0.0 although they are different 
keys
+            if (left instanceof RexInputRef
+                    && right instanceof RexInputRef
+                    && SqlTypeUtil.equalSansNullability(left.getType(), 
right.getType())
+                    && !SqlTypeUtil.isApproximateNumeric(left.getType())) {

Review Comment:
   Good catch, applied. Covered by testJoinUpsertKeyEquivalenceMapNotSatisfied.



-- 
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]

Reply via email to