Skip to content

Commit 95e32b3

Browse files
engleflyYour Name
authored andcommitted
[fix](view) ALTER VIEW definition not synced to follower FE when COMMENT is specified (#61670)
### What problem does this PR solve? before this fix, if alter view statement contains COMMENTS, follower FE will skip updating the view body.
1 parent 1ba5519 commit 95e32b3

File tree

2 files changed

+198
-1
lines changed

2 files changed

+198
-1
lines changed

fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,8 @@ public void replayModifyViewDef(AlterViewInfo alterViewInfo) throws MetaNotFound
898898
String viewName = view.getName();
899899
if (comment != null) {
900900
view.setComment(comment);
901-
} else {
901+
}
902+
if (!Strings.isNullOrEmpty(inlineViewDef)) {
902903
view.setInlineViewDefWithSessionVariables(inlineViewDef, alterViewInfo.getSessionVariables());
903904
view.setNewFullSchema(newFullSchema);
904905
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.doris.alter;
19+
20+
import org.apache.doris.catalog.Column;
21+
import org.apache.doris.catalog.Database;
22+
import org.apache.doris.catalog.Env;
23+
import org.apache.doris.catalog.PrimitiveType;
24+
import org.apache.doris.catalog.View;
25+
import org.apache.doris.nereids.parser.NereidsParser;
26+
import org.apache.doris.nereids.trees.plans.commands.CreateDatabaseCommand;
27+
import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand;
28+
import org.apache.doris.nereids.trees.plans.commands.CreateViewCommand;
29+
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
30+
import org.apache.doris.persist.AlterViewInfo;
31+
import org.apache.doris.qe.ConnectContext;
32+
import org.apache.doris.qe.StmtExecutor;
33+
import org.apache.doris.utframe.UtFrameUtils;
34+
35+
import com.google.common.collect.Lists;
36+
import org.junit.AfterClass;
37+
import org.junit.Assert;
38+
import org.junit.BeforeClass;
39+
import org.junit.Test;
40+
41+
import java.io.File;
42+
import java.util.Collections;
43+
import java.util.HashMap;
44+
import java.util.List;
45+
import java.util.UUID;
46+
47+
/**
48+
* Unit tests for {@link Alter#replayModifyViewDef}.
49+
*
50+
* Covers three replay scenarios:
51+
* 1. Replay a full view definition change (new SQL + new schema).
52+
* 2. Replay a comment-only change (inlineViewDef is empty / null → def must not change).
53+
* 3. Replay with both a new definition and a new comment simultaneously.
54+
*/
55+
public class ReplayModifyViewDefTest {
56+
57+
private static final String RUNNING_DIR =
58+
"fe/mocked/ReplayModifyViewDefTest/" + UUID.randomUUID() + "/";
59+
60+
private static ConnectContext connectContext;
61+
62+
// ──────────────────────────────────────────────────────────────────────────
63+
// Setup / Teardown
64+
// ──────────────────────────────────────────────────────────────────────────
65+
66+
@BeforeClass
67+
public static void beforeClass() throws Exception {
68+
UtFrameUtils.createDorisCluster(RUNNING_DIR);
69+
connectContext = UtFrameUtils.createDefaultCtx();
70+
connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION");
71+
72+
NereidsParser parser = new NereidsParser();
73+
74+
// create database
75+
String createDb = "create database test_replay_view;";
76+
LogicalPlan plan = parser.parseSingle(createDb);
77+
if (plan instanceof CreateDatabaseCommand) {
78+
((CreateDatabaseCommand) plan).run(connectContext, new StmtExecutor(connectContext, createDb));
79+
}
80+
81+
// create table
82+
String createTbl = "create table test_replay_view.tbl1(k1 int, k2 int, v1 int)"
83+
+ " duplicate key(k1) distributed by hash(k1) buckets 1"
84+
+ " properties('replication_num' = '1');";
85+
plan = parser.parseSingle(createTbl);
86+
if (plan instanceof CreateTableCommand) {
87+
((CreateTableCommand) plan).run(connectContext, new StmtExecutor(connectContext, createTbl));
88+
}
89+
90+
// create initial view
91+
String createView = "create view test_replay_view.v1 as select k1, k2 from test_replay_view.tbl1;";
92+
plan = parser.parseSingle(createView);
93+
if (plan instanceof CreateViewCommand) {
94+
((CreateViewCommand) plan).run(connectContext, new StmtExecutor(connectContext, createView));
95+
}
96+
}
97+
98+
@AfterClass
99+
public static void tearDown() {
100+
new File(RUNNING_DIR).delete();
101+
}
102+
103+
// ──────────────────────────────────────────────────────────────────────────
104+
// Helpers
105+
// ──────────────────────────────────────────────────────────────────────────
106+
107+
private static View getView(String viewName) throws Exception {
108+
Database db = Env.getCurrentInternalCatalog().getDbOrDdlException("test_replay_view");
109+
return (View) db.getTableOrDdlException(viewName);
110+
}
111+
112+
private static long getDbId() throws Exception {
113+
return Env.getCurrentInternalCatalog().getDbOrDdlException("test_replay_view").getId();
114+
}
115+
116+
// ──────────────────────────────────────────────────────────────────────────
117+
// Tests
118+
// ──────────────────────────────────────────────────────────────────────────
119+
120+
/**
121+
* Replay a full definition change.
122+
* After replay the view's inlineViewDef and full-schema must reflect the new values.
123+
*/
124+
@Test
125+
public void testReplayModifyViewDefWithNewDef() throws Exception {
126+
View view = getView("v1");
127+
long dbId = getDbId();
128+
long tableId = view.getId();
129+
130+
String newDef = "select k1, v1 from test_replay_view.tbl1 where k1 > 0";
131+
List<Column> newSchema = Lists.newArrayList(
132+
new Column("k1", PrimitiveType.INT),
133+
new Column("v1", PrimitiveType.INT));
134+
135+
AlterViewInfo info = new AlterViewInfo(dbId, tableId, newDef, newSchema,
136+
new HashMap<>(), null);
137+
138+
Env.getCurrentEnv().getAlterInstance().replayModifyViewDef(info);
139+
140+
View updated = getView("v1");
141+
Assert.assertEquals(newDef, updated.getInlineViewDef());
142+
Assert.assertEquals(2, updated.getFullSchema().size());
143+
Assert.assertNotNull(updated.getColumn("k1"));
144+
Assert.assertNotNull(updated.getColumn("v1"));
145+
Assert.assertNull(updated.getColumn("k2"));
146+
}
147+
148+
/**
149+
* Replay a comment-only change (inlineViewDef is null).
150+
* The view definition must remain unchanged; only the comment is updated.
151+
*/
152+
@Test
153+
public void testReplayModifyViewDefCommentOnly() throws Exception {
154+
// First bring v1 back to a known state with two columns.
155+
View view = getView("v1");
156+
long dbId = getDbId();
157+
long tableId = view.getId();
158+
String originalDef = view.getInlineViewDef();
159+
160+
AlterViewInfo info = new AlterViewInfo(dbId, tableId, null,
161+
Collections.emptyList(), new HashMap<>(), "my comment");
162+
163+
Env.getCurrentEnv().getAlterInstance().replayModifyViewDef(info);
164+
165+
View updated = getView("v1");
166+
// Definition must not change.
167+
Assert.assertEquals(originalDef, updated.getInlineViewDef());
168+
// Comment must be set.
169+
Assert.assertEquals("my comment", updated.getComment());
170+
}
171+
172+
/**
173+
* Replay with both a new definition and a comment.
174+
* Both the def/schema and the comment must be updated.
175+
*/
176+
@Test
177+
public void testReplayModifyViewDefWithDefAndComment() throws Exception {
178+
View view = getView("v1");
179+
long dbId = getDbId();
180+
long tableId = view.getId();
181+
182+
String newDef = "select k2 from test_replay_view.tbl1";
183+
List<Column> newSchema = Lists.newArrayList(new Column("k2", PrimitiveType.INT));
184+
185+
AlterViewInfo info = new AlterViewInfo(dbId, tableId, newDef, newSchema,
186+
new HashMap<>(), "updated comment");
187+
188+
Env.getCurrentEnv().getAlterInstance().replayModifyViewDef(info);
189+
190+
View updated = getView("v1");
191+
Assert.assertEquals(newDef, updated.getInlineViewDef());
192+
Assert.assertEquals(1, updated.getFullSchema().size());
193+
Assert.assertNotNull(updated.getColumn("k2"));
194+
Assert.assertEquals("updated comment", updated.getComment());
195+
}
196+
}

0 commit comments

Comments
 (0)