yujun777 commented on code in PR #68648:
URL: https://github.com/apache/doris/pull/68648#discussion_r4144038385


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateMvByPartitionCommand.java:
##########
@@ -130,17 +139,66 @@ private static List<String> 
constructPartsForMv(Set<String> partitionNames) {
         return Lists.newArrayList(partitionNames);
     }
 
+    /**
+     * The predicate every base table of the MV definition is read through.
+     *
+     * <p>A table the caller scopes is read from exactly the base partitions 
it named. Those are the ones
+     * the refresh is about to record as this MV partition's, and the read is 
what has to match the record:
+     * reading the MV partition's own key range instead also reads base 
partitions no snapshot describes,
+     * and a later silent change to one of them -- dropped, with the base 
partition set back to what it
+     * was -- leaves the rows it put in this MV partition behind while the 
partition is still judged
+     * synchronized, so the transparent rewrite serves them and no refresh 
plans it again.
+     *
+     * <p>Every other table keeps the MV partition's own key range, which is 
what the tables the caller
+     * does not scope were always read through. Scoped tables are olap ones; 
the partition names are
+     * looked up on one, see the caller.
+     */
     private static Map<TableIf, Set<Expression>> 
constructTableWithPredicates(MTMV mv,
-            Set<String> partitionNames, Map<TableIf, String> tableWithPartKey) 
throws AnalysisException {
-        Set<PartitionItem> items = Sets.newHashSet();
+            Set<String> partitionNames, Map<TableIf, String> tableWithPartKey,
+            Map<BaseTableInfo, Set<String>> readableBasePartitions) throws 
AnalysisException {
+        Set<PartitionItem> mvItems = Sets.newHashSet();
         for (String partitionName : partitionNames) {
-            PartitionItem partitionItem = 
mv.getPartitionItemOrAnalysisException(partitionName);
-            items.add(partitionItem);
+            mvItems.add(mv.getPartitionItemOrAnalysisException(partitionName));
         }
         ImmutableMap.Builder<TableIf, Set<Expression>> builder = new 
ImmutableMap.Builder<>();
-        tableWithPartKey.forEach((table, colName) ->
-                builder.put(table, constructPredicates(items, colName))
-        );
+        for (Map.Entry<TableIf, String> entry : tableWithPartKey.entrySet()) {
+            TableIf table = entry.getKey();
+            String colName = entry.getValue();
+            Set<String> readable = readableBasePartitions == null ? null
+                    : readableBasePartitions.get(new BaseTableInfo(table));
+            if (readable == null) {
+                builder.put(table, constructPredicates(mvItems, colName));
+                continue;
+            }
+            OlapTable olapTable = (OlapTable) table;
+            Set<PartitionItem> items = Sets.newHashSet();
+            for (String partitionName : readable) {
+                
items.add(olapTable.getPartitionItemOrAnalysisException(partitionName));
+            }
+            if (items.stream().anyMatch(PartitionItem::isDefaultPartition)) {
+                // One of the partitions this MV partition is recorded with is 
a list partitioned table's
+                // default partition, which takes the rows no other partition 
of it claims. Those rows are
+                // the ones the MV partition's own key range names, wherever 
the base table put them, and a
+                // partition of the MV takes them by that key rather than by 
the partition they were placed
+                // in. So a table whose mapped partitions include one is read 
the way an unscoped one is:
+                // the MV partition's key range, at the partition column's own 
type. That read can be seen to
+                // be too wide -- it is the one this scope exists to narrow -- 
rather than one that drops
+                // rows belonging to the MV partition being refreshed. The 
mapping names the default
+                // partition in every MV partition that reads the table, so 
this is reached for each of them
+                // and not only for the one the sentinel key maps to.
+                builder.put(table, constructPredicates(mvItems, colName,
+                        Optional.of(partitionColumnType(olapTable, colName))));
+                continue;
+            }
+            if (readable.isEmpty()) {
+                // No partition of this table feeds the MV partitions being 
refreshed, which is "no row"
+                // rather than "every row": constructPredicates answers the 
other way for an empty set,
+                // and that answer would put every row of the table into each 
of them.
+                builder.put(table, Sets.newHashSet(BooleanLiteral.FALSE));
+                continue;
+            }
+            builder.put(table, constructPredicatesOfBasePartitions(items, 
olapTable, colName));

Review Comment:
   Fixed in d5cbab2190f: the base branch of the compensation pins each 
partition the way a refresh does now -- the whole key of each, at the partition 
column's own type -- so it reads the removed partitions and not the ones whose 
rows the MV branch supplies.
   
   One thing I have to be straight about, because it changes what this reply 
can claim: I could not verify it locally, and not for a subtle reason. The path 
is only reached when the MV is partially usable -- some partition stale or 
excluded -- and in this build `checkMaterializationPattern` refuses such an MV 
outright (`View struct info is invalid`), so the compensation branch never runs 
for the fixtures I built. A fully covering MV of the same shape is `chose` as 
expected, and inserting into one partition turns the same MV into `fail`, which 
is how I know it is the partial usability and not the shape. So the change is 
code-reviewed against what you point at and the refresh path's own pinning, and 
it carries the guard for a default partition (that one is read on the MV's 
partition column, since pinning it to its sentinel key would read none of its 
rows); its end-to-end check has to come from CI or from whoever can build a 
partially usable MV.
   



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateMvByPartitionCommand.java:
##########
@@ -160,30 +223,121 @@ public static Set<Expression> 
constructPredicates(Set<PartitionItem> partitions,
      */
     @VisibleForTesting
     public static Set<Expression> constructPredicates(Set<PartitionItem> 
partitions, Slot colSlot) {
+        return constructPredicates(partitions, colSlot, Optional.empty());

Review Comment:
   Fixed in d5cbab2190f, by the same change as the thread above: the 
compensation's base branch builds its predicate with the partition column's 
full `Type` now, since it shares the refresh path's pinning. The same caveat 
applies to verification: I could not get the compensation branch to run locally 
(a partially usable MV is refused by `checkMaterializationPattern` in this 
build), so this is code-level rather than measured.
   



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

Reply via email to