stevenzwu commented on code in PR #16425:
URL: https://github.com/apache/iceberg/pull/16425#discussion_r4364218879


##########
format/spec.md:
##########
@@ -1600,7 +1603,7 @@ The rows in the delete file must be sorted by `file_path` 
then `pos` to optimize
 
 Equality delete files identify deleted rows in a collection of data files by 
one or more column values, and may optionally contain additional columns of the 
deleted row.
 
-Equality delete files must not be added to v4 tables. Equality deletes cannot 
be added as an entry to a v4 manifest. Equality delete files referenced by v2 
or v3 delete manifests must still be applied in a v4 table.
+Equality delete files must not be added to v4 tables. Equality deletes cannot 
be added as an entry to a v4 manifest. Equality delete files referenced by v2 
or v3 delete manifests must still be applied in a v4 table. Before column files 
are added to a data file, its equality deletes must be written into its 
deletion vector. Adding column files raises the data file's data sequence 
number, so equality delete files no longer apply to it (see [Column 
Files](#column-files)).

Review Comment:
   I would suggest move this to the writer requirement subsection in column 
files.
   
   not just equality deletes. Due to the v4 colocated DV requirement, v2 
position delete files also need to be rewritten to DV. V2 position deletes also 
apply data sequence number filter.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).

Review Comment:
   Maybe simplify it as:
   
   ```suggestion
   A column file stores the entire value of each field in its `field_ids`. Each 
ID must identify a field in the table schema, or a row lineage field required 
by [Adding Column Files](#adding-column-files). A schema field must be 
top-level or nested in structs, and must not be nested within a list or map. A 
field in `field_ids` may also be stored in the data file.
   ```
   
   A field may be nested in structs, but not within a list or map. For example, 
`struct.field` is allowed, but not a field under a struct that is a list 
element or a map value.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.

Review Comment:
   I would move this line/paragraph after line 1685, because line 1685 is 
mainly about the column files (data) and later paragraphs are about metadata 
(manifests).



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files

Review Comment:
   Should we describe writer behavior first (before reader)? it would be easier 
to understand the reader behavior after writer behavior is understood



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files

Review Comment:
   nit: `Adding` -> `Writing`, which would also pair well with `Reading` 
section title above.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.

Review Comment:
   I think the wording of this bullet can be tightened up.
   
   ```suggestion
       - Stats for an updated field and its nested fields must be computed from 
the column file that stores it, or omitted. Stats for other fields should be 
kept.
   ```
   



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.
+- `deletion_vector`:
+    - `deletion_vector` must contain all existing deletes of the data file, 
including equality deletes.
+- `column_files`:
+    - `column_files` contains the previous entry's column files and the new 
column files.
+    - Each updated field ID must be removed from the `field_ids` of the 
previous entry's column files, and a column file with no remaining `field_ids` 
must be removed.
+    - One of the new column files must have the 
`_last_updated_sequence_number` field ID in its `field_ids`. Its value must be 
null for each updated row, and for every other row must be the non-null 
`_last_updated_sequence_number` that a reader of the previous entry would see.
+
+Deletion vectors in delete manifests that reference the data file must be 
removed (see [Deletion Vectors](#deletion-vectors)).

Review Comment:
   I don't think we need this sentence here.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.

Review Comment:
   The intro (line 1661-1671) can be cut down to the scope and the row-count 
requirement. The three terms are only used in Adding Column Files, so define 
them there.
   
   ```
   Starting in v4, a data file may store fields in column files listed in 
[`column_files`](#column-file-fields). A column file is an Iceberg data file 
and must have the same row count as its data file.
   ```
   
   Regarding the terminology bullets, I still think they are trivial. But it 
would be good to get other opinions. They are only used in the `Adding column 
files` sub section. If we keep them, they can be moved there.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.
+- `deletion_vector`:
+    - `deletion_vector` must contain all existing deletes of the data file, 
including equality deletes.
+- `column_files`:
+    - `column_files` contains the previous entry's column files and the new 
column files.
+    - Each updated field ID must be removed from the `field_ids` of the 
previous entry's column files, and a column file with no remaining `field_ids` 
must be removed.
+    - One of the new column files must have the 
`_last_updated_sequence_number` field ID in its `field_ids`. Its value must be 
null for each updated row, and for every other row must be the non-null 
`_last_updated_sequence_number` that a reader of the previous entry would see.

Review Comment:
   This can be simplified.
   
   ```suggestion
       - A new column file must include `_last_updated_sequence_number`, with a 
null value for updated rows and the row's current non-null value for other rows.
   ```
   



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.
+- `deletion_vector`:

Review Comment:
   I would drop this bullet and describe writer requirement for equality delete 
and v2 position deletes in separate paragraph.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.
+- `deletion_vector`:
+    - `deletion_vector` must contain all existing deletes of the data file, 
including equality deletes.
+- `column_files`:
+    - `column_files` contains the previous entry's column files and the new 
column files.

Review Comment:
   I am not we need this bullet. it is also inaccurate. a commit can also 
replace/remove column files. I imagine API should allow such update.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:

Review Comment:
   I don't remember the decision on if updating partition columns is allowed.
   
   If yes, we can have a data file belongs to multiple partitions. This is not 
allowed at least in the current v4 tracked file schema which still has the 
partition tuple, which is only null if unpartitioned.
   
   If we drop the partition tuple in v4 and replaces it with stats, this can 
work. But at least for now, we probably can't do this.



##########
format/spec.md:
##########
@@ -2110,6 +2160,13 @@ Equality deletes are prohibited in v4.
 * Upgrading a v2 or v3 table to v4 does not require rewriting data or delete 
files
 * Readers must continue to apply equality deletes for v2 and v3 tables and for 
equality deletes carried over into upgraded v4 tables
 
+Column updates are added in v4.
+
+* Data files may have column files that store the values of one or more 
fields, with one row for each row of the data file (see [Column 
Files](#column-files))
+* Adding column files to a data file sets its data sequence number to the 
sequence number of the snapshot in which the column files were added

Review Comment:
   this doesn't seem completely correct. the data sequence number can be either 
null (inheritance in leaf manifest.) or set (root manifest).



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.

Review Comment:
   > Values in rows deleted by the MODIFIED entry's `deletion_vector` should be 
null
   
   maybe simplify as `Values for deleted rows should be null`.
   
   not sure if `should` or `must` is better.
   
   > so a top-level field must be optional in the column file's schema if the 
column file stores null for it
   
   Every field in the column file's schema must be optional so that null filler 
values can be set for deleted rows
   
   > For a top-level field that is required in the table schema, a non-null 
value must be stored in every row that is not deleted by the MODIFIED entry's 
`deletion_vector`.
   
   this wording is incorrect. live rows can still have `null` values. we can 
just drop this sentence. it also mentioned top-level field.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.

Review Comment:
   > Row `i` of a column file corresponds to row `i` of the data file. 
   
   I found this sentence redundant. The next sentence says the same thing.
   
   > combined by row position
   
   combined -> aligned
   
   > ignore top-level fields
   
   we agreed that it is not limited to top-level anymore. just `ignore fields`
   
   > A null value in a column file does not indicate that a row is deleted.
   
   I think you probably meant the null filler values for deleted rows. but this 
sentence doesn't seem necessary and achieve any goal. I would be ok to remove 
it.



##########
format/spec.md:
##########
@@ -1653,6 +1656,53 @@ If a delete column in an equality delete file is later 
dropped from the table, i
 
 Manifests hold the same statistics for delete files and data files. For delete 
files, the metrics describe the values that were deleted.
 
+### Column Files
+
+Starting in v4, some fields of a data file can be stored in separate column 
files, listed in the data file's [`column_files`](#column-file-fields), in 
addition to the data file itself. This section describes column files for data 
files.
+
+In this section:
+
+- The **previous entry** is the live entry of the data file before column 
files are added.
+- An **updated field** is a field whose ID is in the `field_ids` of an added 
column file.
+- An **updated row** is a row whose updated-field values were set by the 
update. Other rows keep their current values.
+
+A column file is a valid Iceberg data file: files must use valid Iceberg 
formats, schemas, and column projection.
+
+A column file must have the same row count as its data file.
+
+A column file stores the entire value of each field whose ID is in its 
`field_ids`. Each ID in `field_ids` must be the ID of a top-level field in the 
table schema used to write the column file, or the ID of a row lineage field 
required by [Adding Column Files](#adding-column-files). The `field_ids` of a 
column file may overlap with the field IDs of the data file. Which file a field 
is read from is determined by [Reading Column Files](#reading-column-files).
+
+#### Reading Column Files
+
+A projected field whose top-level field ID is in a column file's `field_ids`, 
including `_last_updated_sequence_number`, must be read from that column file. 
All other projected fields are read from the data file using [Column 
Projection](#column-projection).
+
+Row `i` of a column file corresponds to row `i` of the data file. Column files 
and the data file are combined by row position before the deletion vector is 
applied. Readers must ignore top-level fields in a column file whose IDs are 
not in its `field_ids`. A null value in a column file does not indicate that a 
row is deleted.
+
+#### Adding Column Files
+
+When column files are added to a data file, the writer must produce a MODIFIED 
entry and a REPLACED entry as described in [Updating Tracking 
Metadata](#updating-tracking-metadata). The MODIFIED entry and the REPLACED 
entry may be written to different manifests.
+
+A new column file stores the value of each of its listed fields for every row 
of the data file, including rows the update did not change. Values in rows 
deleted by the MODIFIED entry's `deletion_vector` should be null, so a 
top-level field must be optional in the column file's schema if the column file 
stores null for it. For a top-level field that is required in the table schema, 
a non-null value must be stored in every row that is not deleted by the 
MODIFIED entry's `deletion_vector`.
+
+The `tracking` fields of the MODIFIED entry are set as described in [Updating 
Tracking Metadata](#updating-tracking-metadata) and [Sequence Number 
Inheritance](#sequence-number-inheritance), and `tracking.snapshot_id` must be 
the previous entry's (inherited) snapshot ID. The other fields of the MODIFIED 
entry must match the previous entry, except for the following:
+
+- `spec_id` and `partition`:
+    - If an updated field is, or contains, a source field of the partition 
spec, `spec_id` and `partition` must be null.
+- `sort_order_id`:
+    - If an updated field is, or contains, a field in the sort order, 
`sort_order_id` must be null.
+- `content_stats`:
+    - Stats for each updated field, including its nested fields, must be 
computed from the values in the column file that has its ID in `field_ids`, or 
omitted. Stats for other fields may be kept.
+- `deletion_vector`:
+    - `deletion_vector` must contain all existing deletes of the data file, 
including equality deletes.
+- `column_files`:
+    - `column_files` contains the previous entry's column files and the new 
column files.
+    - Each updated field ID must be removed from the `field_ids` of the 
previous entry's column files, and a column file with no remaining `field_ids` 
must be removed.
+    - One of the new column files must have the 
`_last_updated_sequence_number` field ID in its `field_ids`. Its value must be 
null for each updated row, and for every other row must be the non-null 
`_last_updated_sequence_number` that a reader of the previous entry would see.
+
+Deletion vectors in delete manifests that reference the data file must be 
removed (see [Deletion Vectors](#deletion-vectors)).
+
+Adding column files does not change row IDs (see [Row Lineage](#row-lineage)).

Review Comment:
   I think this line can be removed. Row IDs are already unchanged: the 
MODIFIED entry keeps the previous entry's `first_row_id`, and a column file 
can't contain `_row_id`. 
   
   But we should also update line 497, as It points back here for `_row_id` 
that also forms a circular reference with the Row Lineage section, 
   



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