rdblue commented on code in PR #14234:
URL: https://github.com/apache/iceberg/pull/14234#discussion_r3025182956


##########
format/spec.md:
##########
@@ -707,6 +707,92 @@ For `geography` only, xmin (X value of `lower_bounds`) may 
be greater than xmax
 
 When calculating upper and lower bounds for `geometry` and `geography`, null 
or NaN values in a coordinate dimension are skipped; for example, POINT (1 NaN) 
contributes a value to X but no values to Y, Z, or M dimension bounds. If a 
dimension has only null or NaN values, that dimension is omitted from the 
bounding box. If either the X or Y dimension is missing then the bounding box 
itself is not produced.
 
+#### Content Stats
+
+Iceberg v4 introduces content stats which represent stats in a 
`struct<struct<...>>` where each nested struct holds the stats for an 
individual field of a table. The different field stats types are defined in the 
next section.
+
+##### Field Stats Types
+
+The struct that holds individual stats for a particular field of a table 
consists of the following fields:
+
+| Name             | Type                | Offset from field ID of base struct 
| required | Description                                                        
                                                                                
                            |
+|------------------|---------------------|-------------------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| value_count      | `long`              | 1                                   
| false    | Number of values in the column (including null and NaN values)     
                                                                                
                            |
+| null_value_count | `long`              | 2                                   
| false    | Number of null values in the column                                
                                                                                
                            |
+| nan_value_count  | `long`              | 3                                   
| false    | Number of NaN values in the column                                 
                                                                                
                            |
+| avg_value_count  | `int`               | 4                                   
| false    | The avg value count for variable-length types (string/binary)      
                                                                                
                            |
+| max_value_count  | `long`              | 5                                   
| false    | The max value count for variable-length types (string/binary)      
                                                                                
                            |
+| lower_bound      | type of table field | 6                                   
| false    | Lower bound in the column serialized as the type of the column 
itself. Each value must be less than or equal to all non-null, non-NaN values 
in the column for the file [2]    |
+| upper_bound      | type of table field | 7                                   
| false    | Upper bound in the column serialized as the type of the column 
itself. Each value must be greater than or equal to all non-null, non-NaN 
values in the column for the file [2] |
+| exact_bounds     | `boolean`           | 8                                   
| false    | Whether the `upper_bound` / `lower_bound` is exact or not. 
Defaults to true. Types such as string/binary can't have exact bounds. 
Additionally, if a DV or an equality delete matches a given data file, then 
`exact_bounds` must be treated as `false` |
+
+#### ID Assignment for Stats fields
+
+ID assignment follows a deterministic transform that maps from the **table ID 
space** to the **metadata ID space**. For a given field ID from the **table ID 
space** each nested stats struct gets an ID assigned from the **metadata ID 
space**.
+Offsets defined in the [field stats types section](#field-stats-types) are 
then applied to the stats ID of the enclosing stats struct to calculate IDs for 
each individual field stats type.
+The calculation is: `stats_space_field_id_start_for_data_fields + 
(num_supported_stats_per_column * table_field_id`)
+
+If the table field ID is a [reserved field ID](#reserved-field-ids) then a 
slightly adjusted calculation is used: 
`stats_space_field_id_start_for_metadata_fields + 
(num_supported_stats_per_column * (num_reserved_field_ids - (Integer.MAX_VALUE 
- metadata_field_id))`)
+
+The individual variables are defined as following:
+
+* `stats_space_field_id_start_for_data_fields = 10_000`
+* `stats_space_field_id_start_for_metadata_fields = 2_147_000_000`
+* `num_supported_stats_per_column = 200`, thus supporting a range of **200** 
potential stats fields for a single table field
+* `num_reserved_field_ids = 200` as defined in [reserved field 
ID](#reserved-field-ids)
+
+Note that stats field IDs are only calculated for an assigned field ID:
+
+* that is defined in the table field ID space
+* that is defined in the [reserved field ID](#reserved-field-ids) space
+
+The stats ID range is defined from `10_000` to `200_010_000`, which 
effectively means that the highest supported data field ID is `1_000_000`.
+
+#### Stats projection

Review Comment:
   You can think of the previous section as defining how to produce the type 
for `content_stats`. I think this also requires a section on how to write 
metadata files using those types. The `content_stats` type must be produced for 
a table. That type is embedded in the manifest schema as some field ID. That is 
the schema that is used to write metadata files for the table and to read 
metadata files.
   
   The reason why we need to specify that the schema is used for both writing 
and reading is that this is how type promotion will work. An `int` field named 
`x` will have a struct `x struct<value_count long, lower_bound int, upper_bound 
int, ...>`. When it is promoted to `long`, the new struct will be `x 
struct<value_count long, lower_bound long, upper_bound long>`, so that reading 
metadata will use normal type promotion. Then when writing, the type that was 
read will match the outgoing type (also the struct with `long` bounds).



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