This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 76e295ceb refactor(metadata)!: name the row-lineage column predicate 
explicitly (#2338)
76e295ceb is described below

commit 76e295ceb369efd0e50c8249d13eb70f150b3b0c
Author: Minh Vu <[email protected]>
AuthorDate: Wed Oct 28 18:11:31 2026 +0100

    refactor(metadata)!: name the row-lineage column predicate explicitly 
(#2338)
    
    Signed-off-by: Minh Vu <[email protected]>
---
 metadata_columns.go                     |  7 +++--
 metadata_columns_test.go                | 47 +++++++++++++++++++++++++++++++++
 table/internal/parquet_files.go         |  2 +-
 table/metadata_builder_internal_test.go |  2 +-
 table/metadata_schema_compatibility.go  |  2 +-
 website/src/releases.md                 |  1 +
 6 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/metadata_columns.go b/metadata_columns.go
index 3e2fe4577..eaceb857a 100644
--- a/metadata_columns.go
+++ b/metadata_columns.go
@@ -82,8 +82,11 @@ func LastUpdatedSequenceNumber() NestedField {
        }
 }
 
-// IsMetadataColumn returns true if the field ID is a reserved metadata column 
(e.g. row lineage).
-func IsMetadataColumn(fieldID int) bool {
+// IsRowLineageColumn reports whether fieldID is one of the two row-lineage
+// metadata columns: _row_id or _last_updated_sequence_number.
+// Other reserved metadata field IDs return false. Use [IsReservedFieldID]
+// to check the full range reserved for metadata columns.
+func IsRowLineageColumn(fieldID int) bool {
        return fieldID == RowIDFieldID || fieldID == 
LastUpdatedSequenceNumberFieldID
 }
 
diff --git a/metadata_columns_test.go b/metadata_columns_test.go
new file mode 100644
index 000000000..14b225353
--- /dev/null
+++ b/metadata_columns_test.go
@@ -0,0 +1,47 @@
+// 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.
+
+package iceberg
+
+import (
+       "math"
+       "testing"
+)
+
+func TestIsRowLineageColumn(t *testing.T) {
+       tests := []struct {
+               name    string
+               fieldID int
+               want    bool
+       }{
+               {name: "row ID", fieldID: RowIDFieldID, want: true},
+               {name: "last updated sequence", fieldID: 
LastUpdatedSequenceNumberFieldID, want: true},
+               {name: "position delete file path", fieldID: 
DeleteFilePathFieldID},
+               {name: "position delete position", fieldID: 
DeleteFilePosFieldID},
+               {name: "file metadata column", fieldID: math.MaxInt32 - 1},
+               {name: "other reserved ID", fieldID: MaxStructFieldID + 1},
+               {name: "user column", fieldID: 1},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := IsRowLineageColumn(tt.fieldID); got != 
tt.want {
+                               t.Errorf("IsRowLineageColumn(%d) = %t, want 
%t", tt.fieldID, got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/table/internal/parquet_files.go b/table/internal/parquet_files.go
index 3abe24fc1..c1cc61c37 100644
--- a/table/internal/parquet_files.go
+++ b/table/internal/parquet_files.go
@@ -1672,7 +1672,7 @@ func resolveParquetStatsColumns(meta 
*metadata.FileMetaData, statsCols map[int]S
                        // _last_updated_sequence_number) may legitimately 
appear in a file
                        // without a metrics-plan entry. Writers materialize 
them without
                        // adding them to the table schema, so skip them 
entirely.
-                       if iceberg.IsMetadataColumn(fieldID) {
+                       if iceberg.IsRowLineageColumn(fieldID) {
                                descriptor.skipStats = true
                        } else {
                                descriptor.resolveErr = fmt.Errorf("field id %d 
(column %q) not found in the metrics plan", fieldID, descriptor.path)
diff --git a/table/metadata_builder_internal_test.go 
b/table/metadata_builder_internal_test.go
index 375ed99ef..a740c6f44 100644
--- a/table/metadata_builder_internal_test.go
+++ b/table/metadata_builder_internal_test.go
@@ -3036,7 +3036,7 @@ func TestUnknownTypeValidation(t *testing.T) {
 
 // reservedNonLineageFieldID is a spec-reserved metadata column ID (_file) that
 // is not one of the row-lineage columns, exercising the full reserved range
-// rather than just iceberg.IsMetadataColumn.
+// rather than just iceberg.IsRowLineageColumn.
 const reservedNonLineageFieldID = 2147483646
 
 func TestValidateNoReservedFieldIDs(t *testing.T) {
diff --git a/table/metadata_schema_compatibility.go 
b/table/metadata_schema_compatibility.go
index 62599575e..4c8dc26e7 100644
--- a/table/metadata_schema_compatibility.go
+++ b/table/metadata_schema_compatibility.go
@@ -140,7 +140,7 @@ func checkSchemaCompatibility(sc *iceberg.Schema, 
formatVersion int) error {
                // throwaway metadata from those schemas via AddSchema. User 
schemas are
                // validated against the full reserved range separately, before
                // reassignIDs, in NewMetadataWithUUID.
-               if iceberg.IsMetadataColumn(field.ID) {
+               if iceberg.IsRowLineageColumn(field.ID) {
                        return fmt.Errorf("%w: field '%s' uses reserved 
metadata column ID %d",
                                iceberg.ErrInvalidSchema, colName, field.ID)
                }
diff --git a/website/src/releases.md b/website/src/releases.md
index 80cf90d01..5e74577c3 100644
--- a/website/src/releases.md
+++ b/website/src/releases.md
@@ -78,6 +78,7 @@ Per-release notes (highlights, breaking changes, 
contributors) are published on
 
 - **Partition and sort source accessors renamed.** Replace 
`PartitionField.SourceID()` and `SortField.SourceID()` with `FirstSourceID()`. 
Their behavior and serialized fields are unchanged.
 
+- **Row-lineage column predicate renamed.** Use `iceberg.IsRowLineageColumn` 
instead of `iceberg.IsMetadataColumn` to identify `_row_id` and 
`_last_updated_sequence_number`. Use `iceberg.IsReservedFieldID` to check the 
full reserved metadata field ID range.
 - **File writes and directory walks now take an operation context.** Update 
`WriteFileIO` and `ListableIO` implementations and calls to `Create`, 
`WriteFile`, and `WalkDir` to pass `context.Context`; cloud writes and listings 
use it for cancellation.
 
 ## Verifying and producing releases

Reply via email to