Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,11 @@ private void alterMVRefreshMode(Map<String, String> properties,
}

MaterializedView.RefreshMode finalCurrentRefreshMode = currentRefreshMode;
if (!tableProperty.getMvRefreshMode().equals(mvRefreshMode)) {
// Trigger applier when either the persisted property or the in-memory current mode
// would change. Comparing only the persisted string would miss cases where the two
// are out of sync (e.g. mode promoted internally without rewriting the property).
if (!tableProperty.getMvRefreshMode().equals(mvRefreshMode)
|| materializedView.getCurrentRefreshMode() != finalCurrentRefreshMode) {
appliers.add(() -> {
tableProperty.modifyTableProperties(PropertyAnalyzer.PROPERTIES_MV_REFRESH_MODE, mvRefreshMode);
tableProperty.setMvRefreshMode(mvRefreshMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@
import com.starrocks.warehouse.Warehouse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.EnumUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -724,14 +723,18 @@ public static String analyzeRefreshMode(Map<String, String> properties) {
String refreshMode = null;
if (properties != null && properties.containsKey(PROPERTIES_MV_REFRESH_MODE)) {
refreshMode = properties.get(PROPERTIES_MV_REFRESH_MODE);
MaterializedView.RefreshMode parsed;
try {
MaterializedView.RefreshMode.valueOf(refreshMode.toUpperCase());
parsed = MaterializedView.RefreshMode.valueOf(refreshMode.toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid refresh_mode: " + refreshMode + ". Only " +
EnumUtils.getEnumList(MaterializedView.RefreshMode.class).stream()
.map(MaterializedView.RefreshMode::name)
.collect(Collectors.joining(", ")) +
" are supported.");
throw new IllegalArgumentException("Invalid refresh_mode: " + refreshMode +
". Only INCREMENTAL, PCT are supported.");
}
// AUTO is intentionally not exposed to users; the implementation is preserved
// internally for future revival.
if (parsed == MaterializedView.RefreshMode.AUTO) {
throw new IllegalArgumentException("Invalid refresh_mode: " + refreshMode +
". Only INCREMENTAL, PCT are supported.");
}
properties.remove(PROPERTIES_MV_REFRESH_MODE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,21 @@ public static MaterializedView.RefreshMode getRefreshMode(CreateMaterializedView
}
if (properties.containsKey(PropertyAnalyzer.PROPERTIES_MV_REFRESH_MODE)) {
String mode = properties.get(PropertyAnalyzer.PROPERTIES_MV_REFRESH_MODE);
return MaterializedView.RefreshMode.valueOf(mode.toUpperCase());
MaterializedView.RefreshMode parsed;
try {
parsed = MaterializedView.RefreshMode.valueOf(mode.toUpperCase());
} catch (IllegalArgumentException e) {
throw new SemanticException("Invalid refresh_mode: " + mode +
". Only INCREMENTAL, PCT are supported.");
}
// AUTO is intentionally not exposed to users; the implementation is preserved
// internally for future revival.
if (parsed == MaterializedView.RefreshMode.AUTO) {
throw new SemanticException("Invalid refresh_mode: " + mode +
". Only INCREMENTAL, PCT are supported.");
}
return parsed;
} else {
// Default to INCREMENTAL
return MaterializedView.RefreshMode.PCT;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ public void testChangeMVRefreshMode1() throws Exception {
alterMaterializedView(alterStmt, false);
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());
}
// change refresh mode from incremental to auto
// change refresh mode from incremental to auto: rejected (AUTO is hidden from users)
{
String alterStmt = "alter materialized view test_mv1 set (\"refresh_mode\" = \"auto\")";
alterMaterializedView(alterStmt, false);
Assertions.assertEquals(MaterializedView.RefreshMode.AUTO, mv.getCurrentRefreshMode());
alterMaterializedView(alterStmt, true);
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());
}
// change refresh mode from auto to full
{
Expand Down Expand Up @@ -104,17 +104,17 @@ public void testChangeMVRefreshMode3() throws Exception {
MaterializedView mv = createMaterializedViewWithRefreshMode(query, "incremental");
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());

// change refresh mode from auto to incremental
// alter incremental -> incremental: no-op success
{
String alterStmt = "alter materialized view test_mv1 set (\"refresh_mode\" = \"incremental\")";
alterMaterializedView(alterStmt, false);
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());
}
// change refresh mode from incremental to auto
// alter incremental -> auto: rejected (AUTO is hidden from users)
{
String alterStmt = "alter materialized view test_mv1 set (\"refresh_mode\" = \"auto\")";
alterMaterializedView(alterStmt, false);
Assertions.assertEquals(MaterializedView.RefreshMode.AUTO, mv.getCurrentRefreshMode());
alterMaterializedView(alterStmt, true);
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());
}
// change refresh mode from auto to full
{
Expand Down Expand Up @@ -288,6 +288,28 @@ public void testAutoRefreshInactiveActive3() throws Exception {
}
}

@Test
public void testCreateMVWithAutoRefreshModeRejected() throws Exception {
String ddl = "CREATE MATERIALIZED VIEW `auto_rejected_mv` REFRESH DEFERRED MANUAL " +
"PROPERTIES (\"refresh_mode\" = \"auto\") " +
"AS SELECT id, data, date FROM `iceberg0`.`unpartitioned_db`.`t0` as a;";
Exception ex = Assertions.assertThrows(Exception.class,
() -> starRocksAssert.withMaterializedView(ddl));
Assertions.assertTrue(ex.getMessage().contains("refresh_mode"),
"expected refresh_mode error, got: " + ex.getMessage());
}

@Test
public void testAlterMVRefreshModeToAutoRejected() throws Exception {
String query = "SELECT id, data, date FROM `iceberg0`.`unpartitioned_db`.`t0` as a;";
MaterializedView mv = createMaterializedViewWithRefreshMode(query, "incremental");
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());

String alterStmt = "alter materialized view test_mv1 set (\"refresh_mode\" = \"auto\")";
alterMaterializedView(alterStmt, true);
Assertions.assertEquals(MaterializedView.RefreshMode.INCREMENTAL, mv.getCurrentRefreshMode());
}

private static void checkTableStateToNormal(OlapTable tb) throws InterruptedException {
// waiting table state to normal
int retryTimes = 5;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,28 +291,6 @@ public void testNormal() throws Exception {
}
}

@Test
public void testAutoRefreshModeRejectsPartitionRefresh() throws Exception {
starRocksAssert.withMaterializedView("create materialized view test.mv_auto_to_refresh\n" +
"PARTITION BY k1\n" +
"distributed by hash(k2) buckets 3\n" +
"refresh manual\n" +
"properties (\n" +
"\"refresh_mode\" = \"auto\"\n" +
")\n" +
"as select k1, k2, v1 from test.tbl_with_mv;");
try {
String sql = "REFRESH MATERIALIZED VIEW test.mv_auto_to_refresh " +
"PARTITION START('2022-02-03') END ('2022-02-25') FORCE;";
Exception e = Assertions.assertThrows(Exception.class,
() -> UtFrameUtils.parseStmtWithNewParser(sql, connectContext));
Assertions.assertTrue(e.getMessage().contains(
"Partition refresh is not supported for materialized views with refresh_mode=AUTO."));
} finally {
starRocksAssert.dropMaterializedView("test.mv_auto_to_refresh");
}
}

@Test
public void testIncrementalRefreshModeRejectsPartitionRefresh() throws Exception {
ConnectorPlanTestBase.mockCatalog(connectContext, MockIcebergMetadata.MOCKED_ICEBERG_CATALOG_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,36 @@ protected MaterializedView createMaterializedViewWithRefreshMode(
String refreshMode,
String partitionBy,
Map<String, String> extraProperties) throws Exception {
// AUTO is rejected at the user-input boundary. Test-only bypass: substitute INCREMENTAL
// through the SQL path (which exercises the IVMAnalyzer the same way), then promote
// currentRefreshMode to AUTO. If the query is not IVM-eligible, INCREMENTAL throws;
// fall back to PCT, mirroring AUTO's real create-time fallback.
boolean wantAuto = "auto".equalsIgnoreCase(refreshMode);
String effectiveMode = wantAuto ? "incremental" : refreshMode;

MaterializedView mv;
try {
mv = createMaterializedViewViaSql(query, effectiveMode, partitionBy, extraProperties);
} catch (Exception e) {
if (!wantAuto) {
throw e;
}
mv = createMaterializedViewViaSql(query, "pct", partitionBy, extraProperties);
}
if (wantAuto && mv.getCurrentRefreshMode() == MaterializedView.RefreshMode.INCREMENTAL) {
mv.setCurrentRefreshMode(MaterializedView.RefreshMode.AUTO);
// Persisted refresh_mode property remains "incremental" so DDL regeneration during
// ALTER ACTIVE does not feed "auto" back through IVMAnalyzer (which now rejects it).
// The in-memory currentRefreshMode is what drives processor selection.
}
return mv;
}

private MaterializedView createMaterializedViewViaSql(
String query,
String refreshMode,
String partitionBy,
Map<String, String> extraProperties) throws Exception {
StringBuilder ddl = new StringBuilder();
ddl.append("CREATE MATERIALIZED VIEW `test_mv1` ");
if (partitionBy != null) {
Expand Down
5 changes: 5 additions & 0 deletions test/lib/skip.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,9 @@
'test_window_skew_rewrite_with_mcv',
'test_event_schedule_with_grf',
# 'test_parquet_dict_null_predicate'
# refresh_mode=auto is not exposed to users; these tests exercise the AUTO path via
# SQL and are kept on disk for future revival when AUTO is re-introduced.
'test_ivm_with_iceberg_auto',
'test_ivm_with_iceberg_delete',
'test_ivm_with_iceberg_expire_snapshots',
])
Loading