A column chunk is a run of pages: at most one dictionary page first, then data pages.
Each page is a Thrift PageHeader (compact protocol) followed by the page
data. This page follows
parquet.thrift
and Encodings.md
in the parquet-format repository, read 2026-11-12. Where the chunks are is in the
file metadata.
PageHeader
| ID | Field | Meaning |
|---|---|---|
| 1 | type | 0 DATA_PAGE, 1 INDEX_PAGE (its header is marked “TODO” in the schema), 2 DICTIONARY_PAGE, 3 DATA_PAGE_V2. |
| 2 | uncompressed_page_size | Bytes after decompression, not counting the header. |
| 3 | compressed_page_size | Bytes stored after the header. |
| 4 | crc | Optional standard CRC-32 (polynomial 04C11DB7) of the page as written, after compression and encryption, not counting the header. |
| 5 | data_page_header | For type 0: 1 num_values, 2 encoding, 3 definition_level_encoding, 4 repetition_level_encoding, 5 statistics. |
| 6 | index_page_header | Empty. |
| 7 | dictionary_page_header | 1 num_values, 2 encoding (PLAIN), 3 is_sorted. |
| 8 | data_page_header_v2 | 1 num_values, 2 num_nulls, 3 num_rows, 4 encoding, 5 definition_levels_byte_length, 6 repetition_levels_byte_length, 7 is_compressed (default true), 8 statistics. |
Data page layouts
- Version 1: repetition levels, definition levels, then values; the whole page is compressed together. Each level block (when the column has levels) starts with its 4-byte little-endian length.
- Version 2: repetition levels, then definition levels, both
uncompressed and without length prefixes (their lengths are in the header); then
the values, compressed only if
is_compressed.
Repetition and definition levels
Parquet uses the Dremel encoding with definition and repetition levels (the
README).
The maximum levels follow from the schema and set the number of bits needed to store
them. Repetition levels are not stored for non-nested columns, and definition levels
are skipped for required data. Levels use the RLE hybrid encoding;
BIT_PACKED is no longer used for them.
Encodings
| Value | Encoding | Used for |
|---|---|---|
| 0 | PLAIN | All types; see the physical types on the metadata page. |
| 2 | PLAIN_DICTIONARY | Deprecated name for dictionary encoding in format 1.0 files: dictionary page in PLAIN, indexes as in RLE_DICTIONARY. |
| 3 | RLE | The RLE and bit-packing hybrid: levels, booleans and dictionary indexes. |
| 4 | BIT_PACKED | Deprecated: levels bit-packed most significant bit first. |
| 5 | DELTA_BINARY_PACKED | INT32 and INT64. See below. |
| 6 | DELTA_LENGTH_BYTE_ARRAY | BYTE_ARRAY: all lengths with DELTA_BINARY_PACKED, then all the bytes one after another. |
| 7 | DELTA_BYTE_ARRAY | BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY: lengths of the prefix shared with the previous value (DELTA_BINARY_PACKED), then the suffixes as DELTA_LENGTH_BYTE_ARRAY. |
| 8 | RLE_DICTIONARY | Indexes into the dictionary page: one byte giving the bit width, then the RLE hybrid without a length prefix. |
| 9 | BYTE_STREAM_SPLIT | FLOAT, DOUBLE, INT32, INT64, FIXED_LEN_BYTE_ARRAY: byte i of every value goes into stream i, and the streams follow one another. |
| 10 | ALP | FLOAT and DOUBLE; a preview encoding described in AlpEncoding.md, not covered here. |
Value 1 is not used.
RLE and bit-packing hybrid
A series of runs, each starting with a ULEB128 header:
- Lowest bit 0: an RLE run.
header >> 1is the count; the value follows inceil(bit width / 8)bytes. - Lowest bit 1: a bit-packed run of
(header >> 1) × 8values, packed from the least significant bit of each byte.
Example with bit width 1: 06 01 is an RLE run of three 1s;
03 B5 is one group of eight bit-packed values, 1, 0, 1, 0, 1, 1, 0, 1.
DELTA_BINARY_PACKED
- Header, all ULEB128: values per block (a multiple of 128), miniblocks per block (so that a miniblock holds a multiple of 32 values), total number of values, then the first value as a zigzag ULEB128.
- Each block: the minimum delta (zigzag ULEB128), one byte per miniblock giving its bit width, then the miniblocks: each delta minus the minimum, bit-packed as in the hybrid.
Zigzag maps signed to unsigned: 0 → 0, −1 → 1, 1 → 2, −2 → 3.