CTTY commented on code in PR #2695:
URL: https://github.com/apache/iceberg-rust/pull/2695#discussion_r3574204042


##########
crates/iceberg/src/arrow/record_batch_transformer.rs:
##########
@@ -481,16 +461,39 @@ impl RecordBatchTransformer {
         projected_iceberg_field_ids
             .iter()
             .map(|field_id| {
-                // Check if this is a constant field (metadata/virtual or 
identity-partitioned)
-                // Constant fields always use their pre-computed constant 
values, regardless of whether
-                // they exist in the Parquet file. This is per Iceberg spec 
rule #1: partition metadata
-                // is authoritative and should be preferred over file data.
+                // Check if this is a constant field (metadata/virtual or 
identity-partitioned).
+                //
+                // Metadata/virtual fields (like _file, _spec_id) never exist 
in the data file,
+                // so they always use their pre-computed constant value.
+                //
+                // For identity-partitioned fields, the Iceberg spec's "Column 
Projection" rules
+                // only apply to "field ids which are not present in a data 
file". When the column
+                // IS present in the Parquet file, it must be read from the 
file; the partition
+                // metadata constant is only a fallback for when the column is 
absent (e.g. add_files).

Review Comment:
   Good catch



##########
crates/iceberg/src/arrow/record_batch_transformer.rs:
##########
@@ -359,49 +359,29 @@ impl RecordBatchTransformer {
         let fields: Result<Vec<_>> = projected_iceberg_field_ids
             .iter()
             .map(|field_id| {
-                // Check if this is a constant field
-                if constant_fields.contains_key(field_id) {
-                    // For metadata/virtual fields (like _file), get name from 
metadata_columns
-                    // For partition fields, get name from schema (they exist 
in schema)
-                    if let Ok(iceberg_field) = get_metadata_field(*field_id) {
-                        // This is a metadata/virtual field - convert Iceberg 
field to Arrow
-                        let datum = 
constant_fields.get(field_id).ok_or(Error::new(
-                            ErrorKind::Unexpected,
-                            "constant field not found",
-                        ))?;
-                        let arrow_type = datum_to_arrow_type_with_ree(datum);
-                        let arrow_field =
-                            Field::new(&iceberg_field.name, arrow_type, 
!iceberg_field.required)
-                                .with_metadata(HashMap::from([(
-                                    PARQUET_FIELD_ID_META_KEY.to_string(),
-                                    iceberg_field.id.to_string(),
-                                )]));
-                        Ok(Arc::new(arrow_field))
-                    } else {
-                        // This is a partition constant field (exists in 
schema but uses constant value)
-                        let field = &field_id_to_mapped_schema_map
-                            .get(field_id)
-                            .ok_or(Error::new(ErrorKind::Unexpected, "field 
not found"))?
-                            .0;
-                        let datum = 
constant_fields.get(field_id).ok_or(Error::new(
-                            ErrorKind::Unexpected,
-                            "constant field not found",
-                        ))?;
-                        let arrow_type = datum_to_arrow_type_with_ree(datum);
-                        // Use the type from constant_fields (REE for 
constants)
-                        let constant_field =
-                            Field::new(field.name(), arrow_type, 
field.is_nullable())
-                                .with_metadata(field.metadata().clone());
-                        Ok(Arc::new(constant_field))
-                    }
-                } else {
-                    // Regular field - use schema as-is
-                    Ok(field_id_to_mapped_schema_map
-                        .get(field_id)
-                        .ok_or(Error::new(ErrorKind::Unexpected, "field not 
found"))?
-                        .0
-                        .clone())
+                // Metadata/virtual fields (like _file, _spec_id) don't exist 
in the table
+                // schema, so build their Arrow field from the metadata column 
definition and
+                // the pre-computed constant's type.
+                if let Some(datum) = constant_fields.get(field_id)
+                    && let Ok(iceberg_field) = get_metadata_field(*field_id)
+                {
+                    let arrow_type = datum_to_arrow_type_with_ree(datum);
+                    let arrow_field =
+                        Field::new(&iceberg_field.name, arrow_type, 
!iceberg_field.required)
+                            .with_metadata(HashMap::from([(
+                                PARQUET_FIELD_ID_META_KEY.to_string(),
+                                iceberg_field.id.to_string(),
+                            )]));
+                    return Ok(Arc::new(arrow_field));
                 }
+
+                // Regular fields and identity-partitioned constant fields 
both exist in the
+                // table schema, so use the mapped Arrow field as-is.
+                Ok(field_id_to_mapped_schema_map
+                    .get(field_id)
+                    .ok_or(Error::new(ErrorKind::Unexpected, "field not 
found"))?
+                    .0
+                    .clone())

Review Comment:
   Nice refactoring!



##########
crates/iceberg/src/arrow/reader/pipeline.rs:
##########
@@ -248,6 +250,15 @@ impl FileScanTaskReader {
                 
record_batch_transformer_builder.with_constant(RESERVED_FIELD_ID_FILE, 
file_datum);
         }
 
+        if task
+            .project_field_ids()
+            .contains(&RESERVED_FIELD_ID_SPEC_ID)
+        {
+            let spec_id_datum = 
Datum::int(task.partition_spec.clone().unwrap().spec_id());

Review Comment:
   could the partition spec be none here? what is the expected behavior when 
task.partition_spec is none?
   
   



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