rionmonster commented on code in PR #29583:
URL: https://github.com/apache/flink/pull/29583#discussion_r4497538424


##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/physical/stream/StreamPhysicalMultiJoin.java:
##########
@@ -382,36 +394,51 @@ private String formatInputUniqueKeysWithFieldNames() {
 
     /**
      * This is mainly used in 
`FlinkChangelogModeInferenceProgram.SatisfyUpdateKindTraitVisitor`. If
-     * the unique key of input is a superset of the common join key, then we 
can ignore
-     * UPDATE_BEFORE. Otherwise, we can't ignore UPDATE_BEFORE.
+     * an upsert key of the input contains its common join key and its join 
key, then we can ignore
+     * UPDATE_BEFORE and handle deletes by key. Otherwise, we can't.
      *
-     * <p>For example, if the input schema is [id, name, cnt] with the unique 
key (id) and the
+     * <p>For example, if the input schema is [id, name, cnt] with the upsert 
key (id) and the
      * common join key is (id, name) across joins, then an insert and update 
on the id:
      *
      * <p>+I(1001, Tim, 10) -U(1001, Tim, 10) +U(1001, Timo, 11)
      *
      * <p>If the UPDATE_BEFORE is ignored, the `+I(1001, Tim, 10)` record in 
join will never be
-     * retracted. Therefore, if we want to ignore UPDATE_BEFORE, the unique 
key must contain join
-     * key.
+     * retracted. Therefore, if we want to ignore UPDATE_BEFORE, the upsert 
key must contain the
+     * join key, so that all versions of a record are stored under the same 
key.
      *
-     * <p>This is similar to {@link 
StreamPhysicalJoin#inputUniqueKeyContainsJoinKey(int)} but here
-     * we use the common join key, since the multi join operator partitions on 
the common join key.
+     * <p>This is similar to {@link 
StreamPhysicalJoin#inputUniqueKeyContainsJoinKey(int)}, but the
+     * multi join operator partitions on the common join key and stores each 
input under its own
+     * join key, so the upsert key must contain both.
      */
-    public boolean inputUniqueKeyContainsCommonJoinKey(int inputId) {
-        final RelNode input = getInputs().get(inputId);
-        final Set<ImmutableBitSet> inputUniqueKeysSet = getUniqueKeys(input);
-        if (inputUniqueKeysSet == null || inputUniqueKeysSet.isEmpty()) {
-            return false;
-        }
+    public boolean inputUpsertKeyContainsJoinKey(int inputId) {
+        return !getUpsertKeysContainingJoinKey(inputId).isEmpty();
+    }
 
+    /**
+     * Returns the upsert keys of the input that contain its common join key 
and its join key. An
+     * input without UPDATE_BEFORE replaces the record with the same upsert 
key, which a unique key
+     * that isn't an upsert key doesn't identify.
+     */
+    private List<int[]> getUpsertKeysContainingJoinKey(int inputId) {
         final int[] commonJoinKeyIndices = 
keyExtractor.getCommonJoinKeyIndices(inputId);
         if (commonJoinKeyIndices.length == 0) {
-            return false;
+            return Collections.emptyList();
         }
-
-        final ImmutableBitSet commonJoinKeys = 
ImmutableBitSet.of(commonJoinKeyIndices);
-        return inputUniqueKeysSet.stream()
-                .anyMatch(uniqueKey -> uniqueKey.contains(commonJoinKeys));
+        final RelNode input = inputs.get(inputId);
+        final FlinkRelMetadataQuery fmq =
+                
FlinkRelMetadataQuery.reuseOrCreate(input.getCluster().getMetadataQuery());
+        final Set<ImmutableBitSet> upsertKeys =
+                fmq.getUpsertKeysInKeyGroupRange(input, commonJoinKeyIndices);
+        if (upsertKeys == null) {
+            return Collections.emptyList();

Review Comment:
   Good catch, done (used your UsersRegionsPK example).



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