anoopj commented on code in PR #2985:
URL: https://github.com/apache/iceberg-rust/pull/2985#discussion_r3763819571


##########
crates/iceberg/src/arrow/reader/pipeline.rs:
##########
@@ -301,59 +349,38 @@ impl FileScanTaskReader {
                 .with_constant(RESERVED_FIELD_ID_SPEC_ID, spec_id_datum);
         }
 
-        if task
-            .project_field_ids()
-            .contains(&RESERVED_FIELD_ID_LAST_UPDATED_SEQUENCE_NUMBER)
-        {
-            // A data file may physically carry a per-row 
`_last_updated_sequence_number`
-            // column, e.g. one written by another engine such as Iceberg Java 
when carrying
-            // rows forward across a rewrite. The spec requires reading such 
non-null
-            // per-row values unmodified, falling back to the derived value 
only where
-            // null. That per-row coalesce is not implemented yet, so rather 
than silently
-            // overwrite genuine per-row values with the derived value, reject 
the file
-            // loudly. Checks the full pre-projection file schema, since the 
column is
-            // stripped from the projection mask. Matches on the embedded 
field id when
-            // present (propagating a malformed id rather than treating it as 
absent) and
-            // falls back to the column name, so a file read via name mapping 
or positional
-            // fallback ids (which never equal the reserved id) is still 
caught.
-            let mut file_has_column = false;
-            for field in record_batch_stream_builder.schema().fields() {
-                let field_id = match 
field.metadata().get(PARQUET_FIELD_ID_META_KEY) {
-                    Some(id) => Some(id.parse::<i32>().map_err(|e| {
-                        Error::new(
-                            ErrorKind::DataInvalid,
-                            format!("field id not parseable as an i32: {e}"),
-                        )
-                    })?),
-                    None => None,
-                };
-                if field_id == 
Some(RESERVED_FIELD_ID_LAST_UPDATED_SEQUENCE_NUMBER)
-                    || field.name() == 
RESERVED_COL_NAME_LAST_UPDATED_SEQUENCE_NUMBER
-                {
-                    file_has_column = true;
-                    break;
-                }
-            }
-            if file_has_column {
-                return Err(Error::new(
-                    ErrorKind::FeatureUnsupported,
-                    "Reading a physically-stored _last_updated_sequence_number 
column is \
-                     not yet supported; only the derived 
(data-sequence-number) value is \
-                     implemented",
-                ));
-            }
-
-            // Derive the column, gated on the data file's `first_row_id`. 
Java gates
-            // both lineage columns this way (`ValueReaders.lastUpdated` 
returns nulls
-            // when the base row id is null); the spec itself only says the 
column is
-            // assigned the manifest entry's sequence number on read.
+        if project_last_updated_seq {
+            // Materialize the column, gated on the data file's 
`first_row_id`. Java gates
+            // it this way (`ValueReaders.lastUpdated` returns nulls when the 
base row id is
+            // null); the spec itself only says the column is assigned the 
manifest entry's
+            // sequence number on read.
             record_batch_transformer_builder = match (task.first_row_id, 
task.data_sequence_number)
             {
-                // Non-null first_row_id: inherit from the data sequence 
number.
-                (Some(_), Some(seq)) => 
record_batch_transformer_builder.with_constant(
-                    RESERVED_FIELD_ID_LAST_UPDATED_SEQUENCE_NUMBER,
-                    Datum::long(seq),
-                ),
+                (Some(_), Some(seq)) => {
+                    let datum = Datum::long(seq);
+                    if coalesce_last_updated_seq_leaf.is_some() {
+                        // The file physically carries the column: read the 
per-row value,
+                        // falling back to the data sequence number only where 
null.
+                        
record_batch_transformer_builder.with_coalesced_metadata_column(
+                            RESERVED_FIELD_ID_LAST_UPDATED_SEQUENCE_NUMBER,
+                            datum,
+                        )
+                    } else if last_updated_seq_present_by_name_only {

Review Comment:
   Added the (Some, None) + physical-column test which asserts `DataInvalid`.  
We should not hoist because the transformer keys the source column by field id 
and can't thread an id-less column. But that only matters when we actually read 
the column, which only the `(Some, Some)` arm does. In (None, _) we synthesize 
an all-null column without touching the physical data, so an ordinary 
v2/null-lineage file that happens to carry a same-named, id-less column is 
correctly nulled today. 
   
   Hoisting the reject would turn that correct null into a hard 
FeatureUnsupported. So the reject is arm-local by design; I added a comment 
stating that. If you still want it hoisted I can, but I think arm-local seems 
correct to me.  wdyt?



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