Description
Apache Parquet is a columnar storage format widespread in the Hadoop ecosystem. ClickHouse supports read and write operations for this format.Data types matching
The table below shows how Parquet data types match ClickHouse data types.
When writing Parquet file, data types that don’t have a matching Parquet type are converted to the nearest available type:
Arrays can be nested and can have a value of
Nullable type as an argument. Tuple and Map types can also be nested.
Wide integers
By default, ClickHouse writesInt128, UInt128, Int256, and UInt256 as unannotated FIXED_LEN_BYTE_ARRAY(16/32) values in little-endian order. This legacy representation remains the default so files can be read by older ClickHouse versions.
Set output_format_parquet_wide_integer_as_decimal = 1 to use the standard Parquet DECIMAL representation instead:
The decimal representation provides numeric ordering for standard Parquet column-chunk statistics and page indexes, which lets ClickHouse use row-group and page min/max pruning. ClickHouse reads both encodings. For decimal precision 39, schema inference returns
Decimal(39, 0), backed by Decimal256; specify an explicit Int128 or UInt128 structure to recover a wide-integer type. Decimal precision 77 or 78 exceeds the range of ClickHouse Decimal256, so reading those files requires an explicit compatible Int256 or UInt256 structure. With an explicit wide-integer structure, ClickHouse accepts standard decimal values stored as either BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY of any valid width, and range-checks every value after removing sign extension.
Some Parquet clients support decimal precision only up to 38, while Arrow’s high-level decimal types support precision only up to 76. Such clients can reject the precision-77/78 representation even though its schema and 33-byte physical width conform to Parquet. Keep the setting disabled when files must be read by older ClickHouse versions or clients with smaller decimal limits.
Data types of ClickHouse table columns can differ from the corresponding fields of the Parquet data inserted. When inserting data, ClickHouse interprets data types according to the table above and then casts the data to that data type which is set for the ClickHouse table column. E.g. a UINT_32 Parquet column can be read into an IPv4 ClickHouse column.
For some Parquet types there’s no closely matching ClickHouse type. We read them as follows:
TIMESTAMPwithisAdjustedToUTC=falseis a local wall-clock time (year, month, day, hour, minute, second and subsecond fields in a local timezone, regardless of what specific time zone is considered local), same as SQLTIMESTAMP WITHOUT TIME ZONE. ClickHouse reads it as if it were a UTC timestamp instead. E.g.2025-09-29 18:42:13.000(representing a reading of a local wall clock) becomes2025-09-29 18:42:13.000(DateTime64(3, 'UTC')representing a point in time). If converted to String, it shows the correct year, month, day, hour, minute, second and subsecond, which can then be interpreted as being in some local timezone instead of UTC. Counterintuitively, changing the type fromDateTime64(3, 'UTC')toDateTime64(3)would not help as both types represent a point in time rather than a clock reading, butDateTime64(3)would incorrectly be formatted using local timezone.INTERVALis currently read asFixedString(12)with raw binary representation of the time interval, as encoded in Parquet file.
Variant type
ClickHouse reads columns stored in the ParquetVARIANT encoding, such as those written by Spark 4.0, into the Dynamic type. A column is recognized as a variant if it is a group annotated with the VARIANT logical type (specification version 1). Spark 4.0 writes variant columns without this annotation, so a group with just a metadata and a value field of type BYTE_ARRAY is also read as a variant. This can be disabled with the setting input_format_parquet_detect_variant_by_structure, then such a group is read as a Tuple of the two raw fields, as before; requesting the Dynamic or JSON type for the column explicitly reads it as a variant regardless of the setting.
Each value keeps its own type. Variant primitive types are read as the matching ClickHouse types (for example, int32 as Int32, decimal8 as Decimal(18, S), timestamp as DateTime64(6, 'UTC'), timestamp_ntz as DateTime64(6), binary and string as String, uuid as UUID), the variant null is read as NULL, variant objects are read as JSON with nested objects flattened into dot-separated paths (a null field is treated as absent), variant arrays whose elements are all objects are read as Array(JSON), and other variant arrays are read as Array(Dynamic). The top-level type is Dynamic rather than JSON, because a variant value is not necessarily an object.
A variant column can also be read as JSON by requesting that type explicitly, for example in the table structure or in the structure argument of the file table function. Then every value must be an object, otherwise an exception is thrown. A variant null and a null of the whole column are read as an empty object. Typed paths of the requested JSON type are converted to their types, and paths listed in SKIP and SKIP REGEXP are skipped.
Shredded variants (groups with a typed_value field) are not supported yet: reading such a column throws an exception, while the other columns of the file can still be read, and schema inference skips the column if input_format_parquet_skip_columns_with_unsupported_types_in_schema_inference is enabled. Writing Dynamic or JSON columns to Parquet as VARIANT is not supported either.
File references
A group annotated with the ParquetFILE logical type represents a reference to a range of bytes, stored inline or in an external file. It is read as a named Tuple of the fields present in the file, each of them Nullable: uri, content_type and checksum as Nullable(String), offset and size as Nullable(Int64), and inline as Nullable(String) with raw bytes. A null file reference is read as a tuple with all elements NULL. As for any Tuple, a subset of the fields can be read by requesting a Tuple with fewer elements, or individual fields as subcolumns, such as file.uri. The schema of the group is validated: it must contain only these fields, all OPTIONAL, with the types defined by the specification, otherwise an exception is thrown. The values are not validated: a value that does not form a valid reference (for example, offset without uri) is returned as stored. ClickHouse does not resolve the references, that is, it does not read the external files.
On write, such a Tuple (with element names being a non-empty subset of the FILE fields, integer offset and size, and String or FixedString other elements) is written as a FILE-annotated group if the setting output_format_parquet_write_file_logical_type is enabled. Otherwise it is written as an ordinary group.
Geo types (GeoParquet)
ClickHouse supports reading and writing geometry columns according to the GeoParquet specification. Geometry columns are stored asBYTE_ARRAY payloads encoded in WKB (or WKT on read), with a JSON geo key in the file-level Parquet metadata describing each geometry column’s encoding, geometry type and CRS.
Read behavior
On read, geometry columns are mapped to the corresponding ClickHouse geo data types:- A column declared as
Point,MultiPoint,LineString,Polygon,MultiLineStringorMultiPolygonis read into the matching ClickHouse geo type. - A column with multiple or unknown geometry types is read into the
Geometrytype, which is aVariantover all supported geo types. - If the requested column type is
String, the GeoParquet metadata is ignored and the raw encoded geometry payload is returned as-is — WKB or WKT bytes, matching whichever encoding the GeoParquet column declares. This is also true if the settinginput_format_parquet_allow_geoparquet_parseris set to0.
Write behavior
On write, top-level columns of typePoint, MultiPoint, LineString, Polygon, MultiLineString or MultiPolygon are encoded as BYTE_ARRAY (WKB) and the appropriate geo JSON metadata is appended to the Parquet file footer. A top-level Geometry Variant is also encoded as a WKB BYTE_ARRAY payload (its sub-values are converted to WKB and stored as a Nullable(String) column), but no geo metadata is emitted for it, so the result is not recognized as a GeoParquet geometry column on read. Other geo-related types, such as Ring, are written using their native underlying representation with no GeoParquet metadata. This behavior can be disabled entirely by setting output_format_parquet_geometadata to 0, in which case even the supported geo types are written using their native underlying representation (Point as Tuple(Float64, Float64), LineString as Array(Point), Polygon as Array(Array(Point)), etc.) and no GeoParquet metadata is emitted.
Geometry columns must appear at the root of the schema or nested inside Tuple (struct); nesting them inside Array or Map is not supported. Nullable is not supported for geo columns either.
Example usage
Inserting data
Using a Parquet file with the following data, named asfootball.parquet:
Reading data
Read data using theParquet format:
HDFS table engine.