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


##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRelationManager.java:
##########
@@ -385,28 +396,74 @@ public void alterTable(BaseTableInfo oldTableInfo, 
Optional<BaseTableInfo> newTa
      * rows computed under the old column epoch. Invalidating the MV is what 
keeps that from being
      * reported as current.
      *
+     * <p>The check is the criterion, not just the reason for the record: a 
column the query does not name
+     * is one this change leaves the MV's rows alone for, so nothing is 
invalidated for it. It is a whole
+     * query that is analysed, not a column that is looked up: what the MV can 
no longer be computed from
+     * is what the analysis refuses, wherever in the query it stood.
+     *
      * <p>Every MV is checked, not only an IVM one: whether the query still 
analyzes is a property of
      * the MV and of the base table it reads, not of how the MV refreshes, and 
the invalidation is the
      * same one a change to that table records. What an IVM MV has on top of 
it is a per-partition
      * requirement, and that is decided elsewhere, from a query that analyzed.
      *
-     * @return whether the MV was invalidated. That is the whole record for 
this change: the invalidation
-     *         carries the reason, and the caller has nothing left to write -- 
a second record would land
-     *         on the same state, and MTMVStatus#updateStateAndDetail would 
overwrite the detail with the
-     *         blunter "the base table has been updated", which is what 
knowing the query is unusable is
-     *         for. It would also bump the version and drop the snapshot twice 
for one change.
+     * @return whether the MV was invalidated. False is the answer for a 
change that reaches neither the
+     *         query nor the rows it computed, and it is the whole record for 
that change: there is nothing to
+     *         write, and writing the generic "the base table has been 
updated" anyway would stand for a
+     *         rebuild the MV does not owe.
+     */
+    /**
+     * Whether the query, as it is analysed now, reads a column of any of 
these names.
+     *
+     * <p>The names are matched rather than the columns, and matched 
case-insensitively, because a name is
+     * what the change moves: the column that goes away leaves its name to 
whatever else answers to it, and
+     * the query that reaches the name afterwards is reading a column this 
view's rows were not built from.
      */
-    private boolean invalidateMvIfQueryUnusable(BaseTableInfo baseTableInfo, 
Table mvTable) {
+    private static boolean reachesAnyColumnOf(Plan plan, Set<String> 
columnNames) {
+        if (plan == null) {
+            // A query whose plan was not kept is one this cannot be answered 
about, and "it does" is the
+            // answer that keeps the view safe.
+            return true;
+        }
+        Set<String> names = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
+        names.addAll(columnNames);
+        LineageInfo lineage = LineageInfoExtractor.extractLineageInfo(plan);
+        for (SetMultimap<?, Expression> byType : 
lineage.getDirectLineageMap().values()) {
+            if (reachesAnyColumn(byType.values(), names)) {
+                return true;

Review Comment:
   Fixed in c3b24e67ee1. The per-output copy is gone; the check scans 
`getDatasetIndirectLineageMap().values()` once. The copy the lineage builds for 
each output column holds the same expressions for every column the query 
produces, so it added nothing to this predicate, and the expressions it does 
not carry -- the WINDOW and CONDITIONAL ones -- are reached through the direct 
map's own input slots.
   



##########
fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java:
##########
@@ -373,8 +374,30 @@ private boolean 
processAlterOlapTableInternal(List<AlterOp> alterOps, OlapTable
             throw new DdlException("Invalid alter operations: " + 
currentAlterOps);
         }
         if (needChangeMTMVState(alterOps)) {
-            Env.getCurrentEnv().getMtmvService()
-                .alterTable(oldBaseTableInfo, newBaseTableInfo, 
currentAlterOps.hasReplaceTableOp());
+            // Which columns an operation's effect on a view turns on is the 
operation's to say, see
+            // AlterOp#queryJudgedColumnNames, and every clause of the alter 
has to name them: a batch that
+            // mixes a dropped column with a type change is decided by neither 
-- no query says anything
+            // about a type change -- and stays invalidated the way it was 
before the queries were asked at
+            // all. Each of them also has to have reached the table. A schema 
change that is not a light one
+            // is applied by a job, which may not have run where this hook 
runs: the table still holds the
+            // column the change takes away, every query still analyses 
against it, and an invalidation
+            // decided on that answer would be about the table from before the 
change. What is asked is
+            // whether the change has reached the table, which is the same 
fact the re-analysis reads, so
+            // the two answers cannot disagree.
+            boolean judgedByQuery = alterOps.stream().allMatch(op -> 
!op.queryJudgedColumnNames().isEmpty()

Review Comment:
   Fixed in c3b24e67ee1. `AddColumnOp` and `AddColumnsOp` no longer take the 
default answer: they ask whether the table now holds every column the add is 
about, so an add a job has not applied yet is not judged by a query analysed 
against the schema from before it, and the MV is invalidated the way it was 
before the queries were asked at all.
   
   Why the question is asked per column rather than about the table's shape is 
your case: `SELECT 1 AS flag, COUNT(*) AS n FROM t GROUP BY flag HAVING flag = 
1` goes on analysing while `flag` moves off the alias onto the new base column, 
so what changed is which column answers for the name, and the check decides it 
on that.
   
   Worth recording that my first attempt here was wrong: asking whether the 
columns are in the table instead of asking the change re-invalidated every add 
of a column no query reaches. The suite catches it -- an add of a column the 
query cannot reach must leave the view where it is.
   



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