Skip to content

[Docs][Format] Variant extension: align typed_value primitive type mappings with the Parquet shredding spec #50622

Description

@sdf-jkl

Describe the enhancement requested

The Primitive Type Mappings table in the Variant canonical extension is the normative list of Arrow types allowed for typed_value. The only place the table is referenced from is the typed_value storage rule, so its whole role is defining legal typed_value types:

* A field named ``typed_value`` which can be a :ref:`variant_primitive_type_mapping` or a ``List``, ``LargeList``, ``ListView`` or ``Struct``

The table appears to be derived from the Variant encoding types table (https://github.com/apache/parquet-format/blob/master/VariantEncoding.md#encoding-types), but its role corresponds to the shredding spec's Shredded Value Types table (https://github.com/apache/parquet-format/blob/master/VariantShredding.md#shredded-value-types), which defines what a typed_value column may be. The two tables differ, so the Arrow table currently allows storage with no valid Parquet shredded representation:

In practice the unsigned path already produces non-conformant files: Arrow Go writes unsigned typed_value columns to Parquet with INT(8/16/32, false) annotations, which the shredding spec does not allow.

Proposal: re-derive the table from the shredding spec's Shredded Value Types table. Each row maps one variant type to its Parquet shredded type and the Arrow storage type(s) able to represent its full value domain. The table then defines reconstruction (a typed_value column of the listed Arrow type holds values of exactly the listed variant type) and the write-side lowering (the Parquet column is the only valid Parquet representation).

Variant typeParquet typeArrow typed_value type
booleanBOOLEANBoolean
int8INT32, INT(8, true)Int8
int16INT32, INT(16, true)Int16
int32INT32Int32
int64INT64Int64
floatFLOATFloat32
doubleDOUBLEFloat64
decimal4, 1 <= P <= 9, 0 <= S <= PINT32, DECIMAL(P, S)Decimal32(P, S)
decimal8, 10 <= P <= 18, 0 <= S <= PINT64, DECIMAL(P, S)Decimal64(P, S)
decimal16, 19 <= P <= 38, 0 <= S <= PBYTE_ARRAY / FIXED_LEN_BYTE_ARRAY, DECIMAL(P, S)Decimal128(P, S)
dateINT32, DATEDate32
timeINT64, TIME(false, MICROS)Time64(us)
timestamptz(6)INT64, TIMESTAMP(true, MICROS)Timestamp(us, UTC)
timestamptz(9)INT64, TIMESTAMP(true, NANOS)Timestamp(ns, UTC)
timestampntz(6)INT64, TIMESTAMP(false, MICROS)Timestamp(us)
timestampntz(9)INT64, TIMESTAMP(false, NANOS)Timestamp(ns)
binaryBYTE_ARRAYBinary / LargeBinary / BinaryView
stringBYTE_ARRAY, STRINGUtf8 / LargeUtf8 / Utf8View
uuidFIXED_LEN_BYTE_ARRAY[len=16], UUIDUUID extension type

The decimal precision bands follow the decimal table in VariantEncoding.md: precision alone selects the physical type (the bands are disjoint, so the narrowest type is required), and scale must satisfy 0 <= S <= P. P and S are shared across a row, so the bands constrain both the Parquet DECIMAL annotation and the Arrow type. Arrow otherwise permits negative scale and low-precision wide decimals, and Parquet permits both low-precision wide columns and BYTE_ARRAY decimals above precision 38.

This also resolves three existing ambiguities: the current Time64 row gains its required unit (variant time is micros only), the timestamp rows gain explicit units, and the decimal rows gain their precision bounds.

Related: #50620, apache/parquet-format#591

Component(s)

Format

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions