You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@parquet.apache.org by "Jiayi-Wang-db (via GitHub)" <gi...@apache.org> on 2026/09/21 14:09:11 UTC
[PR] GH-622: Modular footer thrift definition [parquet-format]
Jiayi-Wang-db opened a new pull request, #623:
URL: https://github.com/apache/parquet-format/pull/623
### Rationale for this change
Draft modular footer thrift definition.
### What changes are included in this PR?
### Do these changes have PoC implementations?
<!-- Please uncomment the line below and replace ${GITHUB_ISSUE_ID} with the actual Github issue id. -->
<!-- Closes #${GITHUB_ISSUE_ID} -->
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483497896
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
Review Comment:
encryption seems to live outside the sturct, maybe remove from the comment?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483933936
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
Review Comment:
if we want this to be forward compatible for new row ranges, I think it needs to be a union of Struct.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483748502
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
Review Comment:
nit: I would maybe reverse this
`A value at index 'i' is a dictionary ID, valid range [0, D), for chunk 'i'. Null means the chunk has no dictionary`
I think in the design doc we should discuss the evolution dictionary sharing (we support it as soon as the new footer is supported)?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483894644
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
Review Comment:
we need to clarify if this is mean to be an estimate or exact.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483786390
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
Review Comment:
```suggestion
* True if all data pages use the chunk's dictionary; false when the chunk doesn't have a dictionary..
```
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483879299
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
Review Comment:
if there are other pages besides for dictionaries, those would be included as well. Could we maybe expand the definition/clarify what this is used for. e.g. `The physical size of column chunk in the file.`
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4504025198
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
Review Comment:
This seems like a strange carve out to me.
1. codecs would compress really well with dictionary encoding and the dictionary is easily checkable today.
2. Putting these out of line still allows a reader to do basic parsing if they don't support a codec to get at footer information.
On the balance I think leaving it out of line is better.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483611393
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
Review Comment:
StreamLocation docs seem more appropriate on the struct itself?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483641306
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
Review Comment:
nit: use set size to denote numbers and give an intro to these.
e.g.
```The modular footer layout defines the following cardinalities:
C = |physical columns|
K_c = number of chunks in a column (a chunk is one contiguous set of data for the column)
....
```
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "alamb (via GitHub) via issues" <is...@parquet.apache.org>.
alamb commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4503904000
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
Review Comment:
I likwise don't really understand the reference to "elements" or byte offsets. Gievn it is a struct, I don't understand the reference to i32 either 🤔
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483843419
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
Review Comment:
what does excluding dictionaries mean here? I think a lot of other values above are non-negative, is there a reason you are specifically calling it out here?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483572313
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
Review Comment:
Can we please try to use complete sentences.
e.g. a reasonable place to start for documenting this struct IIUC is "An in-file reference to series of parquet pages that store parquet metadata. e.g. [optional DICTIONARY_PAGE][DATA_PAGE (V1 OR V2) ...]" Or are these specifically V2 data pages? We should document how the allowed set is controlled/versioned.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483685706
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
Review Comment:
I think these constrains probably only make sense once you describe what column_chunk_index actually is in english.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on PR #623:
URL: https://github.com/apache/parquet-format/pull/623#issuecomment-6713944191
Could we make sure we setup build infrastructure so we can ensure valid thrift.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486796420
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
Review Comment:
I think an example or more docs here would be useful, I don't understand what it is supposed to contain.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486788429
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
Review Comment:
nit: `Compact-Thrift plaintext` seems confusing, I guess this to distinguish from compressed?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4484077150
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
Review Comment:
I think a little bit more english explanation would be useful here for arbitrary.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4485848089
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
Review Comment:
I guess the only other addition I can think of here would be "fixed" width. so maybe we don't need forward compatibility but it would be a shame to break readers in the future just because they couldn't decode a stats enum.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483919897
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
Review Comment:
Why this rather then two independent streams? We expect most columns tstats columns to be sorted?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483526672
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
Review Comment:
Should the encryption details be documented on the encryption structures. I assum the 28 bytes are incorporated into the lenght here so we don't need to know the detail?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483650539
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
+
+ /** Total bytes: LE length4 (excludes itself) + nonce12 + ciphertext + GCM-tag16.
+ * Plaintext type: ColumnRowRangeStatistics.
+ */
+ 4: required i64 length;
+}
+
+/** Omit when no encrypted modules exist. Listed columns have zero shared
+ * ARBITRARY ranges or unknown shared COLUMN_CHUNK/PAGE slots; keep positions.
+ * Footer-key statistics may use encrypted shared streams; separate column keys
+ * require independent blocks. With signed streams, protected statistics also
+ * require blocks. Missing keys mean unavailable statistics; authentication
+ * failure or malformed plaintext is an error.
+ */
+struct EncryptedRowRangeStatistics {
+ /** Sorted by column_ordinal; no duplicate columns or overlapping blocks. */
+ 1: required list<EncryptedColumnRowRangeStatistics> columns;
+}
+
+// Footer directory.
+
+/** Visible stream directory, including column-key descriptors.
+ * Layout: [source data][metadata streams and column-statistics blocks]
+ * [Compact-Thrift root][28-byte signature if crypto_metadata is present]
+ * [u32 LE directory length][PAR3]. Length includes the root and optional
+ * signature, excluding the 8-byte trailer. The root starts at
+ * file_size - 8 - directory_length; validate bounds before subtraction.
+ * Streams/blocks precede the root and must not overlap.
+ * See the PME section for authentication and AAD rules.
+ */
+struct ModularFooter {
Review Comment:
nit: if thrift supports it, we maybe should put this struct first so readers can see the high level view when reading the Thrift file.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483774069
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
Review Comment:
I'm not sure I understand "including its codec". Is there a use-case for the dictionary being incompatible (i.e. does the sentence add value)?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "alamb (via GitHub) via issues" <is...@parquet.apache.org>.
alamb commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4503917608
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
Review Comment:
is this for each page? I don't understand how this is related to the `PageMetadata` below
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
Review Comment:
The [document](https://docs.google.com/document/d/1xN4Nqh-fh-eH-cUz9e6Rqs_gPXRBKUh4s29EzX_NU8M/edit?tab=t.cbl5qtgc5xhz#heading=h.44x8xo35gxa6) says
> "The Modular Footer allows each column to have its own number of chunks, with boundaries independent of row groups and other columns. "
If we are getting rid of row groups, why do we have RowGroupMetadata 🤔
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
Review Comment:
If we are changing ColumnChunk to store a sequence of pages and associated information, I don't understand the use of `PageMetadata` here
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
+
+ /** Total bytes: LE length4 (excludes itself) + nonce12 + ciphertext + GCM-tag16.
+ * Plaintext type: ColumnRowRangeStatistics.
+ */
+ 4: required i64 length;
+}
+
+/** Omit when no encrypted modules exist. Listed columns have zero shared
+ * ARBITRARY ranges or unknown shared COLUMN_CHUNK/PAGE slots; keep positions.
+ * Footer-key statistics may use encrypted shared streams; separate column keys
+ * require independent blocks. With signed streams, protected statistics also
+ * require blocks. Missing keys mean unavailable statistics; authentication
+ * failure or malformed plaintext is an error.
+ */
+struct EncryptedRowRangeStatistics {
+ /** Sorted by column_ordinal; no duplicate columns or overlapping blocks. */
+ 1: required list<EncryptedColumnRowRangeStatistics> columns;
+}
+
+// Footer directory.
+
+/** Visible stream directory, including column-key descriptors.
+ * Layout: [source data][metadata streams and column-statistics blocks]
Review Comment:
I don't really understand a lot of this 🤔 like what does
> [u32 LE directory length][PAR3]
mean? If it is trying to explain the layout of the footer, I think an ascii art diagram would be more useful
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
Review Comment:
I wonder if inlining the codecs would be valuable as they are small and could let a reader quickly figure out if they supported something or not
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483506117
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
Review Comment:
should this be its own stream, so we can change representation in the future?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483670020
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
Review Comment:
It seems like this could be distributed more locally for the most part.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483470243
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
+
+ /** Total bytes: LE length4 (excludes itself) + nonce12 + ciphertext + GCM-tag16.
+ * Plaintext type: ColumnRowRangeStatistics.
+ */
+ 4: required i64 length;
+}
+
+/** Omit when no encrypted modules exist. Listed columns have zero shared
+ * ARBITRARY ranges or unknown shared COLUMN_CHUNK/PAGE slots; keep positions.
+ * Footer-key statistics may use encrypted shared streams; separate column keys
+ * require independent blocks. With signed streams, protected statistics also
+ * require blocks. Missing keys mean unavailable statistics; authentication
+ * failure or malformed plaintext is an error.
+ */
+struct EncryptedRowRangeStatistics {
+ /** Sorted by column_ordinal; no duplicate columns or overlapping blocks. */
+ 1: required list<EncryptedColumnRowRangeStatistics> columns;
+}
+
+// Footer directory.
+
+/** Visible stream directory, including column-key descriptors.
+ * Layout: [source data][metadata streams and column-statistics blocks]
+ * [Compact-Thrift root][28-byte signature if crypto_metadata is present]
+ * [u32 LE directory length][PAR3]. Length includes the root and optional
+ * signature, excluding the 8-byte trailer. The root starts at
+ * file_size - 8 - directory_length; validate bounds before subtraction.
+ * Streams/blocks precede the root and must not overlap.
+ * See the PME section for authentication and AAD rules.
+ */
+struct ModularFooter {
+ 1: required i32 version; // Allocation pending.
+
+ /** Exactly one of file_info and encrypted_file_info must be present. */
+ 2: optional FileInfo file_info;
+ 3: required DataLayout data_layout;
+ 4: required DictionaryMetadata dictionary_metadata;
+ 5: optional RowGroupMetadata row_group_metadata;
+ 6: required ColumnChunkMetadata column_chunk_metadata;
+ 7: optional PageMetadata page_metadata;
+
+ /** Any number of independent granularities, preferably coarse to fine.
+ * Boundaries need not nest; match by row interval, not list position.
+ * Do not sum overlapping counts. Omit sets with no available statistics;
+ * omit the list when none remain.
+ */
+ 8: optional list<SharedRangeStatistics> row_range_statistics;
+
+ /** BYTE_ARRAY; number of values = M; nullability: non-null. */
+ 9: optional StreamLocation key_value_metadata_keys;
+
+ /** BYTE_ARRAY; number of values = M; nullability: nullable. Aligned with keys. */
+ 10: optional StreamLocation key_value_metadata_values;
+
+ /** Algorithm/AAD parameters and opaque footer-key metadata.
Review Comment:
At least a few of the comments read a little bit like AI slop, could you please do a pass to make them more human readable.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483826582
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
Review Comment:
there was a recent thread on the mailing list about clarifying this. I think one conclusion we should note is that this value is equal to the number of definition/repetition levels written for the chunk when they apply (and it is equal to row count if the column is not nested and non nullable).
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483805639
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
Review Comment:
What does omission mean if not-null.
we should note this is the encoded size, this has been a source of confusion in the existing specificatin.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "alamb (via GitHub) via issues" <is...@parquet.apache.org>.
alamb commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4503905684
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
Review Comment:
Yeah I am very confused upon first reading.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483489839
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
+
+ /** Total bytes: LE length4 (excludes itself) + nonce12 + ciphertext + GCM-tag16.
+ * Plaintext type: ColumnRowRangeStatistics.
+ */
+ 4: required i64 length;
+}
+
+/** Omit when no encrypted modules exist. Listed columns have zero shared
+ * ARBITRARY ranges or unknown shared COLUMN_CHUNK/PAGE slots; keep positions.
+ * Footer-key statistics may use encrypted shared streams; separate column keys
+ * require independent blocks. With signed streams, protected statistics also
+ * require blocks. Missing keys mean unavailable statistics; authentication
+ * failure or malformed plaintext is an error.
+ */
+struct EncryptedRowRangeStatistics {
+ /** Sorted by column_ordinal; no duplicate columns or overlapping blocks. */
+ 1: required list<EncryptedColumnRowRangeStatistics> columns;
+}
+
+// Footer directory.
+
+/** Visible stream directory, including column-key descriptors.
+ * Layout: [source data][metadata streams and column-statistics blocks]
+ * [Compact-Thrift root][28-byte signature if crypto_metadata is present]
+ * [u32 LE directory length][PAR3]. Length includes the root and optional
+ * signature, excluding the 8-byte trailer. The root starts at
+ * file_size - 8 - directory_length; validate bounds before subtraction.
+ * Streams/blocks precede the root and must not overlap.
+ * See the PME section for authentication and AAD rules.
+ */
+struct ModularFooter {
+ 1: required i32 version; // Allocation pending.
+
+ /** Exactly one of file_info and encrypted_file_info must be present. */
Review Comment:
nit: maybe place encrypted file_info next to file info?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483532740
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
Review Comment:
This detail seems like it belongs in ColumnRowRangeStatistics?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4484055334
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
Review Comment:
Also, the single letters can maybe be expanded a little bit to avoid making readers back-reference specific variables.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4485840623
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
Review Comment:
nit: maybe a more descriptive name PageSequenceLocation? Maybe too verbose?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486774619
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
Review Comment:
Oh is this because this is pointing to the encrypted thrift metadata?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486756585
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
Review Comment:
should we be re-using stream here?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483446993
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
Review Comment:
this doc makes no sense to me. could you try rephrasing, in a way that is useful to readers that are not faimiliar with the implementation?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483541635
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
Review Comment:
What does root protection mean here?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4483572313
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
Review Comment:
Can we please try to use complete sentences.
e.g. a reasonable place to start for documenting this struct IIUC is "An in-file reference to series of parquet pages that store parquet metadata. The type of the data encoded varies by reference and is documented per field below. e.g. [optional DICTIONARY_PAGE][DATA_PAGE (V1 OR V2) ...]" Or are these specifically V2 data pages? We should document how the allowed set is controlled/versioned.
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486711182
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
+ 3: required i64 offset;
+
+ /** Total bytes: LE length4 (excludes itself) + nonce12 + ciphertext + GCM-tag16.
+ * Plaintext type: ColumnRowRangeStatistics.
+ */
+ 4: required i64 length;
+}
+
+/** Omit when no encrypted modules exist. Listed columns have zero shared
+ * ARBITRARY ranges or unknown shared COLUMN_CHUNK/PAGE slots; keep positions.
+ * Footer-key statistics may use encrypted shared streams; separate column keys
+ * require independent blocks. With signed streams, protected statistics also
+ * require blocks. Missing keys mean unavailable statistics; authentication
+ * failure or malformed plaintext is an error.
+ */
+struct EncryptedRowRangeStatistics {
+ /** Sorted by column_ordinal; no duplicate columns or overlapping blocks. */
+ 1: required list<EncryptedColumnRowRangeStatistics> columns;
+}
+
+// Footer directory.
+
+/** Visible stream directory, including column-key descriptors.
+ * Layout: [source data][metadata streams and column-statistics blocks]
+ * [Compact-Thrift root][28-byte signature if crypto_metadata is present]
+ * [u32 LE directory length][PAR3]. Length includes the root and optional
+ * signature, excluding the 8-byte trailer. The root starts at
+ * file_size - 8 - directory_length; validate bounds before subtraction.
+ * Streams/blocks precede the root and must not overlap.
+ * See the PME section for authentication and AAD rules.
+ */
+struct ModularFooter {
+ 1: required i32 version; // Allocation pending.
+
+ /** Exactly one of file_info and encrypted_file_info must be present. */
+ 2: optional FileInfo file_info;
+ 3: required DataLayout data_layout;
+ 4: required DictionaryMetadata dictionary_metadata;
+ 5: optional RowGroupMetadata row_group_metadata;
+ 6: required ColumnChunkMetadata column_chunk_metadata;
+ 7: optional PageMetadata page_metadata;
+
+ /** Any number of independent granularities, preferably coarse to fine.
+ * Boundaries need not nest; match by row interval, not list position.
+ * Do not sum overlapping counts. Omit sets with no available statistics;
+ * omit the list when none remain.
+ */
+ 8: optional list<SharedRangeStatistics> row_range_statistics;
+
+ /** BYTE_ARRAY; number of values = M; nullability: non-null. */
Review Comment:
should we have a member for M here, or we rely on the number of values in the page? Maybe package into its own struct. If M is only used here do we need to have an abbreviation for it or can we just document it?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486753306
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
+}
+
+// Parquet Modular Encryption (PME) structures.
+
+/** Metadata uses AES-GCM under both PME algorithms. Use a fresh nonce per key
+ * for every invocation. Sign the exact serialized root (nonce12 + GCM-tag16;
+ * discard ciphertext); authenticate before trusting it or decrypting FileInfo.
+ * Verify each stream before parsing pages. Enforce the caller's protection
+ * policy; removed crypto metadata must not downgrade protection.
+ *
+ * AAD = PME file AAD (prefix + aad_file_unique) followed by:
+ * directory: NEW_FOOTER_DIRECTORY_TYPE + LE32(version).
+ * FileInfo: NEW_FILE_INFO_TYPE + LE32(version).
+ * stream: NEW_METADATA_STREAM_TYPE + LE32(version) + LE32(path length)
+ * + LE32(each path component) + LE64(offset) + LE64(length) + LE32(codec).
+ * column stats: NEW_ROW_RANGE_STATISTICS_TYPE + LE32(version) + LE32(column ordinal).
+ * Paths use Thrift field IDs and zero-based list positions from the root;
+ * e.g. [8,g,2,7] identifies granularity g's min_max_int32. Component kinds
+ * follow the schema. Module-type allocations are pending; do not reuse PME IDs.
+ */
+
+/** Compact-Thrift plaintext for one column-statistics module.
+ * All ranges belong to the enclosing column_ordinal. COLUMN_CHUNK uses S=K_c;
+ * PAGE uses this column's page count. Both reuse authenticated shared hierarchy
+ * and row metadata; ARBITRARY stores local row starts, without column_range_starts.
+ * Only the column's physical-type bound stream may be present (2S values).
+ * All StreamLocation offsets are relative to page_streams.
+ */
+struct ColumnRowRangeStatistics {
+ /** Nonempty list of independent granularities, with RangeStatistics semantics.
+ * Positions are local to this module; all entries are encrypted together.
+ */
+ 1: required list<RangeStatistics> statistics;
+
+ /** All located streams, concatenated and encoded/compressed before encryption;
+ * each uses the PageHeader/body format defined by StreamLocation.
+ */
+ 2: required binary page_streams;
+
+ /** One order per statistics entry; omitted or unavailable orders mean UNORDERED. */
+ 3: optional list<parquet.BoundaryOrder> boundary_orders;
+}
+
+/** One independently encrypted module per physical column. */
+struct EncryptedColumnRowRangeStatistics {
+ /** Physical leaf ordinal in FileInfo.schema; 0 <= column_ordinal < C. */
+ 1: required i32 column_ordinal;
+
+ /** Exactly one PME key selector, no raw key; applies to the entire column.
+ * ENCRYPTION_WITH_COLUMN_KEY: path_in_schema must match column_ordinal;
+ * resolve the column DEK using its key metadata.
+ * ENCRYPTION_WITH_FOOTER_KEY: use the footer DEK from root crypto_metadata.
+ */
+ 2: required parquet.ColumnCryptoMetaData crypto_metadata;
+
+ /** Absolute offset of the module's length prefix, before the root. */
Review Comment:
what does before the root here mean?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org
Re: [PR] GH-622: Modular footer thrift definition [parquet-format]
Posted by "emkornfield (via GitHub) via issues" <is...@parquet.apache.org>.
emkornfield commented on code in PR #623:
URL: https://github.com/apache/parquet-format/pull/623#discussion_r4486735102
##########
src/main/thrift/ModularFooter.thrift:
##########
@@ -0,0 +1,415 @@
+/**
+ * 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.
+ */
+
+/** Modular footer supporting flexible data layouts and row-range statistics.
+ *
+ * C = physical columns; K_c = chunks in column c; K = sum(K_c).
+ * P_k = data pages in chunk k; P = sum(P_k); D = dictionaries; G = row groups.
+ * M = key/value pairs. K_c may vary, including zero, independently of G.
+ * Chunk membership alone does not determine row coverage.
+ *
+ * StreamLocation comments specify the scalar Parquet type, value count and
+ * nullability. Each stream is a primitive column with no LIST nesting.
+ * Field IDs define types; no stream schema is serialized.
+ * FileInfo.schema describes the source data.
+ * Thrift optional controls stream presence, not value nullability.
+ * Non-null streams: max RL=DL=0. Nullable streams: max RL=0, max DL=1
+ * (DL=0: null; DL=1: present). No hierarchy repetition levels are stored.
+ *
+ * Chunk fields have K positions in column/chunk order; page fields have P
+ * positions in chunk/page order. Nulls keep their positions. Shared indexes
+ * address logical positions; metadata-page boundaries may differ by stream.
+ * These streams need not have equal row counts or form one Parquet row group.
+ * Validate counts, indexes and extents; authenticate/decrypt required streams
+ * before use. An unavailable index never means an empty hierarchy.
+ */
+include "parquet.thrift"
+namespace cpp parquet.modular
+namespace java org.apache.parquet.format.modular
+
+// Shared metadata structures.
+
+/** PageHeader/body pairs: [optional DICTIONARY_PAGE][DATA_PAGE_V2 ...].
+ * Shared streams inherit root protection, including headers and dictionaries.
+ * Streams inside ColumnRowRangeStatistics are plaintext within its encrypted module.
+ */
+struct StreamLocation {
+ /** Absolute file offset; relative to page_streams inside a column module. */
+ 1: required i64 offset;
+
+ /** Stored bytes, including headers and protection. Signed streams append a
+ * 28-byte signature; encrypted streams add length4 + nonce12 + GCM-tag16.
+ * Column-module streams have no individual protection overhead.
+ */
+ 2: required i64 length;
+
+ /** Codec for dictionary bodies and V2 values when is_compressed is true.
+ * Levels are uncompressed. Compress before protection; independent of data codecs.
+ */
+ 3: required parquet.CompressionCodec codec;
+}
+
+/** Exclusive element ends, not byte offsets; non-negative signed i32 values.
+ * Omit only the fixed leading zero; retain genuine zero ends (empty lists).
+ * boundary(index,n) = 0 if n=0, else index[n-1], for 0<=n<=length.
+ */
+struct DataLayout {
+ /** INT32; number of values = C; nullability: non-null.
+ * C must match FileInfo.num_columns. Non-decreasing; last end is K
+ * (K=0 if C=0). Column c owns [boundary(index,c), index[c]) chunks.
+ */
+ 1: required StreamLocation column_chunk_index;
+
+ /** INT32; number of values = K; nullability: non-null.
+ * Non-decreasing; last end is P (P=0 if K=0).
+ * Chunk k owns [boundary(index,k), index[k]) pages.
+ * Required with PageMetadata; otherwise optional. If absent, use source
+ * page headers for grouping; absence does not imply P=0.
+ */
+ 2: optional StreamLocation page_index;
+}
+
+/** Dictionary IDs address table positions. */
+struct DictionaryMetadata {
+ /** INT64; number of values = D; nullability: non-null. D fits signed i32.
+ * Absolute dictionary-page offsets, including headers. Store each referenced
+ * dictionary once; no adjacency/order restriction. First-reference order is
+ * recommended. Both streams are empty when no dictionaries are used.
+ */
+ 1: required StreamLocation offsets;
+
+ /** INT64; number of values = D; nullability: non-null.
+ * Positive stored sizes, including headers. Dictionary d occupies
+ * [offsets[d], offsets[d]+lengths[d]), disjoint from data and other dictionaries.
+ */
+ 2: required StreamLocation lengths;
+}
+
+struct RowGroupMetadata {
+ /** INT64; number of values = G; nullability: non-null.
+ * Row counts in row-group order; sum equals FileInfo.num_rows.
+ */
+ 1: required StreamLocation num_rows;
+
+ /** INT64; number of values = G+1; nullability: non-null.
+ * Non-decreasing absolute byte boundaries; optional hint for contiguous
+ * row groups in file order. Omit if interleaving or shared dictionaries
+ * prevent complete extents. Chunk/dictionary locations remain independent.
+ */
+ 2: optional StreamLocation offsets;
+}
+
+/** Flat fields grouped by DataLayout.column_chunk_index. */
+struct ColumnChunkMetadata {
+ /** INT32; number of values = K; nullability: non-null.
+ * parquet.CompressionCodec values.
+ */
+ 1: required StreamLocation codecs;
+
+ /** INT32; number of values = K; nullability: nullable.
+ * Null means no dictionary; otherwise an ID in [0,D). Sharing is allowed
+ * only within one physical column, as determined by column_chunk_index.
+ * Every dictionary must be referenced and compatible with each referencing
+ * chunk, including its codec.
+ */
+ 2: required StreamLocation dictionary_ids;
+
+ /** BOOLEAN; number of values = K; nullability: non-null.
+ * True if all data pages use the chunk's dictionary; false without one.
+ */
+ 3: required StreamLocation is_fully_dictionary_encoded;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Page headers + uncompressed bodies, counting the dictionary once per
+ * referencing chunk, even if shared. Omission means unknown sizes.
+ */
+ 4: optional StreamLocation total_uncompressed_sizes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Data-value counts including nulls; not row counts.
+ */
+ 5: required StreamLocation num_values;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute first rows; required for any COLUMN_CHUNK statistics, including
+ * encrypted statistics. Independent of row groups and num_values; must agree
+ * with the chunk's first page row when page-row metadata is present.
+ */
+ 6: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Absolute offsets of contiguous data pages, including headers, excluding
+ * dictionaries. Chunks need not be adjacent or in byte-offset order.
+ */
+ 7: required StreamLocation offsets;
+
+ /** INT64; number of values = K; nullability: non-null.
+ * Stored data-page bytes, excluding dictionaries; non-negative, zero iff
+ * no data pages. [offsets[k], offsets[k]+compressed_sizes[k]) extents are
+ * disjoint when nonempty. Check file bounds and overflow; never infer size
+ * from another chunk or dictionary.
+ */
+ 8: required StreamLocation compressed_sizes;
+}
+
+/** Independently optional streams sharing DataLayout.page_index; omit if both absent. */
+struct PageMetadata {
+ /** INT64; number of values = P; nullability: non-null.
+ * Absolute first rows; required for any PAGE statistics, including encrypted
+ * statistics. If absent, obtain row positions from source-data pages.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = P; nullability: non-null.
+ * Relative to ColumnChunkMetadata.offsets[k]. Start at 0 in each nonempty
+ * chunk and strictly increase below compressed_sizes[k]; last page ends at
+ * the chunk end. Retain the first zero, including for single-page chunks.
+ * If absent, scan source page headers to locate interior pages.
+ */
+ 2: optional StreamLocation relative_offsets;
+}
+
+/** Source of a statistics set's row ranges. */
+enum RowRangeKind {
+ COLUMN_CHUNK = 0,
+ PAGE = 1,
+ ARBITRARY = 2
+}
+
+/** S ranges for one granularity; the containing struct defines column ownership.
+ * Null values or omitted statistics streams mean unknown, including all-nullness.
+ * Counts are non-negative. When known, null_count + nan_count <= num_values.
+ */
+struct RangeStatistics {
+ /** INT64; number of values = S; nullability: non-null.
+ * Absolute first rows; present iff range_kind is ARBITRARY. Per column,
+ * start at 0 and strictly increase below FileInfo.num_rows. End at the next
+ * start or FileInfo.num_rows; alignment with chunks/pages/row groups is optional.
+ */
+ 1: optional StreamLocation first_row_indexes;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of null values per range.
+ */
+ 2: optional StreamLocation null_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Number of NaN values per range. NaNs are non-null values.
+ */
+ 3: optional StreamLocation nan_count;
+
+ /** INT64; number of values = S; nullability: nullable.
+ * Total data values per range, including nulls and NaNs; not a row count.
+ */
+ 4: optional StreamLocation num_values;
+
+ /** INT64; number of values = S; nullability: nullable. */
+ 5: optional StreamLocation distinct_counts;
+
+ /** Bound streams: [min_0, max_0, min_1, max_1, ...]; values independently nullable.
+ * Wrong physical type or unavailable bound means null. Pairs may cross
+ * metadata-page boundaries.
+ */
+ 6: optional StreamLocation min_max_boolean; // BOOLEAN; number of values = 2S.
+ 7: optional StreamLocation min_max_int32; // INT32; number of values = 2S.
+ 8: optional StreamLocation min_max_int64; // INT64; number of values = 2S.
+ 9: optional StreamLocation min_max_float; // FLOAT; number of values = 2S.
+ 10: optional StreamLocation min_max_double; // DOUBLE; number of values = 2S.
+ 11: optional StreamLocation min_max_byte_array; // BYTE_ARRAY; number of values = 2S.
+
+ /** BOOLEAN; number of values = 2S; nullability: nullable.
+ * Aligned with paired bounds: true=exact, false=inexact, null=unknown.
+ * An unavailable bound requires null exactness.
+ */
+ 12: optional StreamLocation min_max_is_exact;
+
+ /** Writers emit the tag; absent means ARBITRARY. Unknown tags are unsupported.
+ * COLUMN_CHUNK: one range per chunk; requires ColumnChunkMetadata.first_row_indexes.
+ * PAGE: one range per page; requires PageMetadata.first_row_indexes and both
+ * DataLayout indexes. Each participating chunk must have at least one page.
+ * ARBITRARY: explicit first_row_indexes; used when positional coverage is unsuitable.
+ *
+ * For each participating column, ranges must be nonempty, row-aligned and
+ * partition [0, FileInfo.num_rows). Starts begin at 0 and strictly increase;
+ * each end is the next start or num_rows. Starts alone do not prove gap coverage.
+ * These rules also apply to encrypted statistics. Authenticate/decrypt the
+ * referenced hierarchy and row streams under their own policy before use.
+ */
+ 13: optional RowRangeKind range_kind = RowRangeKind.ARBITRARY;
+}
+
+/** One shared granularity across C columns; streams inherit root protection.
+ * COLUMN_CHUNK (S=K) and PAGE (S=P) retain every position, using unknown slots
+ * for unavailable or independently encrypted statistics. ARBITRARY permits
+ * zero ranges per column via equal column_range_starts.
+ */
+struct SharedRangeStatistics {
+ /** INT64; number of values = C+1; nullability: non-null.
+ * Present iff statistics.range_kind is ARBITRARY. Non-decreasing, from 0 to S.
+ * Column c owns [index[c], index[c+1]); equal ends mean no ranges.
+ */
+ 1: optional StreamLocation column_range_starts;
+
+ /** For column c, let a=boundary(column_chunk_index,c), b=column_chunk_index[c].
+ * COLUMN_CHUNK: [a,b). PAGE: [boundary(page_index,a),boundary(page_index,b)).
+ * ARBITRARY: use column_range_starts. boundary() is defined in DataLayout.
+ */
+ 2: required RangeStatistics statistics;
+
+ /** INT32; number of values = C; nullability: non-null.
+ * parquet.BoundaryOrder per column. Columns without shared statistics must
+ * be UNORDERED; omission means all UNORDERED.
+ */
+ 3: optional StreamLocation boundary_orders;
+}
+
+/** Inline file metadata, plaintext or encrypted with the footer key. */
+struct FileInfo {
+ 1: required i32 num_columns; // Physical columns.
+ 2: required i64 num_rows;
+ 3: required list<parquet.SchemaElement> schema;
+ 4: optional string created_by;
Review Comment:
maybe we should move key value tags here, so it is clear they need to be encrypted when the footer is encrypted?
--
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: issues-unsubscribe@parquet.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@parquet.apache.org
For additional commands, e-mail: issues-help@parquet.apache.org