This is an automated email from the ASF dual-hosted git repository.
Gabriel39 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 3763f098979 [fix](fe) Skip table snapshot resolution for external
views (#69836)
3763f098979 is described below
commit 3763f0989794c14906e56757eb975d314918697a
Author: daidai <[email protected]>
AuthorDate: Tue Nov 3 10:26:41 2026 +0800
[fix](fe) Skip table snapshot resolution for external views (#69836)
### What problem does this PR solve?
External views currently resolve a table handle when loading the
statement snapshot, before their SQL is expanded. Skip that table-only
path for known views; their base tables retain normal snapshot pinning.
Reject unsupported view scan parameters such as `@branch` and `@tag`
instead of silently ignoring the requested selector.
### Release note
Avoid table metadata requests for external views during snapshot
binding, and reject unsupported view scan parameters.
### Check List (For Author)
For this update: Checkstyle passed for both changed Java files.
Selector-rejection unit tests were added but not run locally.
- Test
- [x] Regression test
- [x] Unit Test
- [x] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason
- Behavior changed:
- [ ] No.
- [x] Yes.
- Does this need documentation?
- [x] No.
- [ ] Yes.
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
---
.../mvcc/PluginDrivenMvccExternalTable.java | 11 ++++
.../mvcc/PluginDrivenMvccExternalTableTest.java | 69 ++++++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTable.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTable.java
index f12616cfd76..84fbdc2f58e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTable.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTable.java
@@ -505,6 +505,17 @@ public class PluginDrivenMvccExternalTable extends
PluginDrivenExternalTable
Optional<TableSnapshot> tableSnapshot,
Optional<TableScanParams> scanParams,
Optional<MvccSnapshot> latestSnapshotFence) {
+ // Views have no table snapshot. BindRelation expands their SQL and
pins the base tables;
+ // resolving a table handle here would load the view name through the
table API.
+ if (isView()) {
+ // View expansion cannot apply relation scan parameters to the
base tables.
+ if (scanParams.isPresent()) {
+ throw new UnsupportedOperationException(
+ "view not supported with scan params: @" +
scanParams.get().getParamType());
+ }
+ return new PluginDrivenMvccSnapshot(emptySnapshot(),
+ Collections.emptyMap(), Collections.emptyMap());
+ }
if (!tableSnapshot.isPresent() && !scanParams.isPresent()) {
// B5a implicit query-begin (latest) pin.
return latestSnapshotFence.isPresent()
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTableTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTableTest.java
index f2f9fc645f2..15a35c77df0 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTableTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTableTest.java
@@ -901,6 +901,75 @@ public class PluginDrivenMvccExternalTableTest {
// ==================== loadSnapshot: B5a latest materialize
====================
+ @Test
+ public void testViewSnapshotDoesNotUseTableMetadata() {
+ Fixture f = Fixture.partitioned();
+ PluginDrivenMvccExternalTable view = Mockito.spy(f.table);
+ Mockito.doReturn(true).when(view).isView();
+ // A catalog may support the view endpoint but reject table loads for
this name. Query binding
+ // must not depend on whether the unrelated table request returns 404
or another error.
+ Mockito.when(f.metadata.getTableHandle(f.session, "REMOTE_DB",
"REMOTE_TBL"))
+ .thenThrow(new IllegalStateException("table endpoint is
unavailable for this view"));
+ PluginDrivenMvccSnapshot fence = new PluginDrivenMvccSnapshot(
+
ConnectorMvccSnapshot.builder().snapshotId(PINNED_SNAPSHOT_ID).build(),
+ Collections.emptyMap(), Collections.emptyMap());
+
+ List<MvccSnapshot> snapshots = Arrays.asList(
+ view.loadSnapshot(Optional.empty(), Optional.empty()),
+ view.loadSnapshot(Optional.empty(), Optional.empty(),
Optional.empty()),
+ view.loadSnapshot(Optional.empty(), Optional.empty(),
Optional.of(fence)));
+
+ for (MvccSnapshot snapshot : snapshots) {
+ PluginDrivenMvccSnapshot pin =
Assertions.assertInstanceOf(PluginDrivenMvccSnapshot.class, snapshot);
+ Assertions.assertEquals(-1L,
pin.getConnectorSnapshot().getSnapshotId());
+ Assertions.assertNull(pin.getPinnedSchema());
+ Assertions.assertTrue(pin.getNameToPartitionItem().isEmpty());
+ Assertions.assertTrue(pin.getNameToLastModifiedMillis().isEmpty());
+ Assertions.assertSame(f.latestCacheValue,
view.getSchemaCacheValue(Optional.of(pin)).get());
+ }
+ Mockito.verifyNoInteractions(f.metadata);
+ }
+
+ @Test
+ public void testViewSnapshotRejectsScanParamsWithoutTableMetadata() {
+ Fixture f = Fixture.partitioned();
+ PluginDrivenMvccExternalTable view = Mockito.spy(f.table);
+ Mockito.doReturn(true).when(view).isView();
+ PluginDrivenMvccSnapshot fence = new PluginDrivenMvccSnapshot(
+
ConnectorMvccSnapshot.builder().snapshotId(PINNED_SNAPSHOT_ID).build(),
+ Collections.emptyMap(), Collections.emptyMap());
+ Map<String, String> incrementalParams = new HashMap<>();
+ incrementalParams.put("startSnapshotId", "1");
+ incrementalParams.put("endSnapshotId", "5");
+ List<TableScanParams> selectors = Arrays.asList(
+ new TableScanParams(TableScanParams.BRANCH,
+ Collections.emptyMap(),
Collections.singletonList("b1")),
+ new TableScanParams(TableScanParams.BRANCH,
+ Collections.singletonMap(TableScanParams.PARAMS_NAME,
"b1"), Collections.emptyList()),
+ new TableScanParams(TableScanParams.TAG,
+ Collections.emptyMap(),
Collections.singletonList("t1")),
+ new TableScanParams(TableScanParams.TAG,
+ Collections.singletonMap(TableScanParams.PARAMS_NAME,
"t1"), Collections.emptyList()),
+ new TableScanParams(TableScanParams.INCREMENTAL_READ,
incrementalParams, Collections.emptyList()),
+ new TableScanParams(TableScanParams.OPTIONS,
+ Collections.singletonMap("scan.snapshot-id", "7"),
Collections.emptyList()));
+
+ for (TableScanParams selector : selectors) {
+ List<UnsupportedOperationException> errors = Arrays.asList(
+
Assertions.assertThrows(UnsupportedOperationException.class,
+ () -> view.loadSnapshot(Optional.empty(),
Optional.of(selector))),
+
Assertions.assertThrows(UnsupportedOperationException.class,
+ () -> view.loadSnapshot(Optional.empty(),
Optional.of(selector), Optional.empty())),
+
Assertions.assertThrows(UnsupportedOperationException.class,
+ () -> view.loadSnapshot(Optional.empty(),
Optional.of(selector), Optional.of(fence))));
+ for (UnsupportedOperationException error : errors) {
+ Assertions.assertEquals("view not supported with scan params:
@" + selector.getParamType(),
+ error.getMessage());
+ }
+ }
+ Mockito.verifyNoInteractions(f.metadata);
+ }
+
@Test
public void testLoadSnapshotEmptyMaterializes() {
Fixture f = Fixture.partitioned();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]