github-actions[bot] commented on code in PR #67904:
URL: https://github.com/apache/doris/pull/67904#discussion_r3998709927


##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java:
##########
@@ -733,9 +734,9 @@ public Optional<ConnectorMvccSnapshot> resolveTimeTravel(
                 // latestSnapshot() only).
                 Table branchTable = 
resolveTable(paimonHandle.withBranch(branchName));
                 long snapshotId = 
catalogOps.latestSnapshotId(branchTable).orElse(-1L);
-                long schemaId = snapshotId < 0
-                        ? -1L
-                        : catalogOps.snapshotSchemaId(branchTable, 
snapshotId).orElse(-1L);
+                // A schema-only ALTER advances the branch schema without 
creating a data snapshot.
+                // Keep the data fence but bind the branch's current schema 
instead of that snapshot's old schema.
+                long schemaId = -1L;

Review Comment:
   [P1] Enforce the recorded branch data fence. This code records the branch 
latest snapshot id, but applySnapshot handles the branch sentinel first and 
returns withBranch(branch) without carrying that id; withBranch clears the 
transient table, so split planning and JNI serialization reload the then-latest 
branch with no scan.snapshot-id, and an empty branch has no empty marker. A 
commit between binding and planning can therefore change the rows observed by 
the statement. Please carry branch identity together with the recorded 
positive/empty fence while retaining the current branch schema, and add a 
mutation test between resolution and planning.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonScanPlanProvider.java:
##########
@@ -346,7 +346,13 @@ Table resolveScanTable(PaimonTableHandle paimonHandle) {
         Map<String, String> scanOptions = paimonHandle.getScanOptions();
         Table finalTable = table;
         if (scanOptions != null && !scanOptions.isEmpty()) {
-            if (PaimonScanParams.isOptionsPin(scanOptions)) {
+            if (table instanceof FileStoreTable
+                    && PaimonScanParams.preservesBoundSchema(scanOptions)) {

Review Comment:
   [P1] Preserve the bound schema for schema-derived system wrappers too. A 
selector-free statement-fenced @options carries this marker, but 
$ro/$audit_log/$binlog/$row_tracking are wrappers rather than FileStoreTable 
instances, so they miss this arm and fall through to applyOptions/Table.copy, 
rewinding a fresh S2 wrapper to the data snapshot S1 schema. Metadata binds S1, 
the scan path can rebuild S2 from the preserve-aware source, and 
tableForBackend serializes S1 again. Schema-only add/rename queries therefore 
fail or disagree even without a stale cache. Please apply the provenance to the 
source before building the wrapper in every binding/planning/backend path and 
add a supported system-alias mutation test.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonScanPlanProvider.java:
##########
@@ -346,7 +346,13 @@ Table resolveScanTable(PaimonTableHandle paimonHandle) {
         Map<String, String> scanOptions = paimonHandle.getScanOptions();
         Table finalTable = table;
         if (scanOptions != null && !scanOptions.isEmpty()) {
-            if (PaimonScanParams.isOptionsPin(scanOptions)) {
+            if (table instanceof FileStoreTable
+                    && PaimonScanParams.preservesBoundSchema(scanOptions)) {
+                // A statement fence owns data visibility, not schema time 
travel. Reusing Table.copy
+                // here would roll schema-only ALTERs back to the data 
snapshot's older schema.
+                finalTable = PaimonScanParams.applyOptionsWithoutTimeTravel(
+                        (FileStoreTable) table, scanOptions);

Review Comment:
   [P1] Carry the exact schema Doris bound, not merely a preserve boolean. 
getTableSchema deliberately reads schemaManager().latest() because Paimon 
cached Table.rowType() is frozen, but copyWithoutTimeTravel keeps the schema 
embedded in this table object. After warming S1 and applying an external 
schema-only ALTER to S2, analysis binds S2 while this path still plans and 
serializes S1; @options fails the missing-column check and JNI cannot resolve 
the requested S2 field. Please carry the exact bound schema generation into 
this chokepoint and add a warm-cache mutation test covering native planning and 
serialized/JNI reads; the new tests reload an uncached table after ALTER and 
mask this case.



##########
regression-test/suites/external_table_p0/paimon/test_paimon_schema_only_snapshot_precision.groovy:
##########
@@ -0,0 +1,124 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_paimon_schema_only_snapshot_precision", "p0,external,paimon") {
+    String enabled = context.config.otherConfigs.get("enablePaimonTest")
+    if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+        logger.info("disable paimon test")
+        return
+    }
+
+    String minioPort = context.config.otherConfigs.get("iceberg_minio_port")
+    String externalEnvIp = context.config.otherConfigs.get("externalEnvIp")
+    String catalogName = "test_paimon_schema_only_snapshot_precision"
+    String dbName = "paimon_schema_only_snapshot_precision_db"
+    String tableName = "schema_only_timeline"
+    String branchName = "schema_only_branch"
+
+    def latestSnapshotId = {
+        List<List<Object>> rows = spark_paimon """
+            select snapshot_id
+            from paimon.${dbName}.`${tableName}\$snapshots`
+            order by snapshot_id desc
+            limit 1
+        """
+        assertEquals(1, rows.size())
+        return rows[0][0].toString()
+    }
+
+    sql """drop catalog if exists ${catalogName}"""
+    sql """
+        create catalog ${catalogName} properties (
+            'type'='paimon',
+            'warehouse'='s3://warehouse/wh',
+            's3.endpoint'='http://${externalEnvIp}:${minioPort}',
+            's3.access_key'='admin',
+            's3.secret_key'='password',
+            's3.path.style.access'='true',
+            'meta.cache.paimon.table.ttl-second'='0'
+        )
+    """
+
+    try {
+        spark_paimon_multi """
+            create database if not exists paimon.${dbName};
+            drop table if exists paimon.${dbName}.${tableName};
+            create table paimon.${dbName}.${tableName} (
+                id int,
+                old_name string,
+                event_time timestamp

Review Comment:
   [P2] Make this column TIMESTAMP_NTZ. This suite runs Spark 4.0 with the 
default spark.sql.timestampType=TIMESTAMP_LTZ; Paimon maps that to 
LocalZonedTimestampType, which this converter intentionally does not push. Both 
assertions can therefore pass through Doris residual filtering even if the new 
Timestamp.fromLocalDateTime precision fix is reverted. Please declare 
event_time TIMESTAMP_NTZ and use a matching NTZ literal/cast, or set and 
restore the Spark setting, so the regression exercises the changed NTZ pruning 
path.



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