924060929 commented on code in PR #67904:
URL: https://github.com/apache/doris/pull/67904#discussion_r4004077344


##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java:
##########
@@ -100,6 +101,10 @@ public class PaimonConnectorMetadata implements 
ConnectorMetadata {
     // existing direct-construction tests compile unchanged; production goes 
through the 5-arg ctor.
     private final PaimonLatestSnapshotCache latestSnapshotCache;
 
+    // Metadata is statement-scoped: aliases sharing a data fence must also 
share one schema generation.

Review Comment:
   The captured schema is owned by one ConnectorMetadata instance, but an 
INSERT planning retry resets the connector scope while retaining the source 
MVCC snapshot. The replacement metadata has an empty statementSchemas map, so a 
preserved latest pin with a schema ID falls through to the catalog-lifetime 
schemaAtMemo. With an old table generation still memoized and a recreated 
same-name table reusing that schema ID, attempt 1 can bind the new table while 
attempt 2 obtains old column handles and fails. Please make the exact captured 
schema live as long as the retained MVCC pin, or re-read it once in the 
replacement scope without consulting the historical memo. Add an INSERT replan 
regression with an old-generation memo.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonScanParams.java:
##########
@@ -196,14 +201,40 @@ public static FileStoreTable 
applyOptionsWithoutTimeTravel(
                     .filter(key -> !tableOptions.containsKey(key))
                     .forEach(key -> isolatedOptions.put(key, null));
         }
-        // The statement fence already selected the schema generation. 
Preserve that generation
-        // while carrying only the resolved read selector and execution 
options into this copy.
+        String schemaId = options.get(BOUND_SCHEMA_ID);
+        if (schemaId != null && table.schema().id() != 
Long.parseLong(schemaId)) {
+            table = restoreBoundSchema(table, Long.parseLong(schemaId));
+        }
         FileStoreTable effectiveTable = (FileStoreTable) 
PaimonReaderOptions.runtimeSafeTable(
                 table.copyWithoutTimeTravel(isolatedOptions));
         PaimonReaderOptions.validateEffectiveTable(effectiveTable);
         return effectiveTable;
     }
 
+    private static FileStoreTable restoreBoundSchema(FileStoreTable table, 
long schemaId) {
+        if (table instanceof FallbackReadFileStoreTable) {
+            FallbackReadFileStoreTable pair = (FallbackReadFileStoreTable) 
table;
+            // Schema IDs are branch-local. Broadcasting the main schema 
through copy(TableSchema)
+            // overwrites the fallback's provenance and can even reset its 
branch to main.
+            return new 
FallbackReadFileStoreTable(restoreBoundSchema(pair.wrapped(), schemaId), 
pair.fallback());
+        }
+        if (table instanceof DelegatedFileStoreTable) {
+            FileStoreTable wrapped = ((DelegatedFileStoreTable) 
table).wrapped();
+            return PaimonTableDecorators.replaceWrapped(table, 
restoreBoundSchema(wrapped, schemaId));
+        }
+        TableSchema bound = table.schemaManager().schema(schemaId);
+        Map<String, String> persisted = 
table.schemaManager().schema(table.schema().id()).options();
+        Map<String, String> merged = new HashMap<>(bound.options());
+        // Field-referencing options evolve with the schema (e.g. bucket-key 
and sequence.field
+        // on rename). Only replay the catalog/runtime delta, never another 
generation's options.
+        table.options().forEach((key, value) -> {

Review Comment:
   Value equality cannot recover option provenance. For example, an explicit 
catalog read.batch-size=64 is indistinguishable here from an old physical value 
of 64. After an external option-only change makes the physical value 128, this 
filter drops the explicit catalog override and the scan incorrectly uses 128; a 
new value of 0 can instead make a previously valid query fail validation. 
Preserve explicit catalog/runtime overrides separately and merge bound physical 
options, then catalog/runtime overrides, then relation overrides.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonScanParams.java:
##########
@@ -196,14 +201,40 @@ public static FileStoreTable 
applyOptionsWithoutTimeTravel(
                     .filter(key -> !tableOptions.containsKey(key))
                     .forEach(key -> isolatedOptions.put(key, null));
         }
-        // The statement fence already selected the schema generation. 
Preserve that generation
-        // while carrying only the resolved read selector and execution 
options into this copy.
+        String schemaId = options.get(BOUND_SCHEMA_ID);
+        if (schemaId != null && table.schema().id() != 
Long.parseLong(schemaId)) {
+            table = restoreBoundSchema(table, Long.parseLong(schemaId));
+        }
         FileStoreTable effectiveTable = (FileStoreTable) 
PaimonReaderOptions.runtimeSafeTable(
                 table.copyWithoutTimeTravel(isolatedOptions));
         PaimonReaderOptions.validateEffectiveTable(effectiveTable);
         return effectiveTable;
     }
 
+    private static FileStoreTable restoreBoundSchema(FileStoreTable table, 
long schemaId) {
+        if (table instanceof FallbackReadFileStoreTable) {
+            FallbackReadFileStoreTable pair = (FallbackReadFileStoreTable) 
table;
+            // Schema IDs are branch-local. Broadcasting the main schema 
through copy(TableSchema)
+            // overwrites the fallback's provenance and can even reset its 
branch to main.
+            return new 
FallbackReadFileStoreTable(restoreBoundSchema(pair.wrapped(), schemaId), 
pair.fallback());

Review Comment:
   This restores only the main child and keeps the cached fallback child 
unchanged. If a warm M0/F0 fallback table has since received the same 
compatible nullable ADD COLUMN on both real branches, the current state is 
M1/F1 but Doris constructs M1/F0. Paimon 1.3.1 validates the complete row types 
before projection, so even SELECT id fails although the external branches are 
compatible. Capture and restore each child using its own branch-local schema 
coordinate; a main schema ID cannot be broadcast to the fallback branch.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/mvcc/PluginDrivenMvccExternalTable.java:
##########
@@ -172,6 +172,19 @@ private PluginDrivenMvccSnapshot materializeLatest(
         // legacy listPartitions/LIST/timestamp path below (byte-unchanged; 
the no-op applySnapshot for the
         // latest pin is side-effect-free for both paimon and iceberg).
         ConnectorTableHandle pinnedHandle = metadata.applySnapshot(session, 
handle, connectorSnapshot);
+        PluginDrivenSchemaCacheValue pinnedSchema = null;
+        if (connectorSnapshot.getSchemaId() >= 0) {
+            // Latest data and schema can advance independently. Keep the 
connector's exact schema
+            // on the statement pin so analysis cannot fall back to a 
different cached generation.
+            ConnectorTableSchema atSchema = metadata.getTableSchema(session, 
pinnedHandle, connectorSnapshot);

Review Comment:
   The newly retained latest schema calls the snapshot-aware metadata overload. 
Through an HMS catalog, Hive delegates that overload to the Iceberg sibling but 
does not apply the capability reflection used by the ordinary two-argument 
overload. The resulting pinned schema has an empty capability set and shadows 
the enriched schema, disabling TopN lazy materialization, nested-column 
pruning, and storage predicate inference for ordinary HMS Iceberg reads. Please 
reuse the existing sibling capability inheritance helper in the snapshot-aware 
Hive path and add a retained-latest publication test for all three capabilities.



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