Release Notes¶
See GitHub Releases for downloads and more information.
1.1.0.Beta3 (2026-10-21)¶
Bug fixes:
- The native CLI rejects TLS certificates issued under intermediate CAs whose private keys have been compromised, including with a trust store passed via
-Djavax.net.ssl.trustStore(#1604)
1.1.0.Beta2 (2026-10-11)¶
Announcement blog post · API changes
Highlights of this release:
- Written files support row-group and page skipping
- A page index (
ColumnIndexandOffsetIndex) per column chunk, with pages of at mostWriterConfig.pageTargetRowsrecords - Split-block Bloom filters for the columns named in
WriterConfig.Builder.bloomFilter(...) encoding_stats, so a reader can prune row groups by their dictionary
- A page index (
- Further writer additions
ParquetFileWriter.endRowGroup()closes a row group at a boundary the caller choosesParquetFileWriter.abort()abandons a write, andclose()after a failed write discards the output instead of publishing a partial fileRowWriter.tryWriteRow(...)reports a rejected record without failing the writerOutputFile.inMemory()writes a file to memory
- Fewer requests on remote reads
- The page index is fetched for the projected columns only, for several row groups per request
- Bloom filters are fetched as the read reaches a row group, together with those of the neighbouring row groups
- Opening a file takes no separate request for the leading magic, and the next row group's first chunk is prefetched
- A multi-file read opens each file as it reaches it, so time to first row no longer grows with the number of files
- Parsed footers can be reused across readers
- A
MetadataSourceinstalled throughHardwoodContext.builder()supplies the parsed footer of every file a reader opens, so a cached footer spares the footer read and parse InputFile.identity()(the S3 ETag, or size, modification time and file key locally) detects a changed file, which raisesStaleMetadataException
- A
- Dictionary-encoded batches on
ColumnReadergetDictionaryIds()andgetBinaryDictionary()expose each value's dictionary entry forBYTE_ARRAY,FIXED_LEN_BYTE_ARRAYandINT96columns- Binary values are read through per-value views into the batch buffer, built only when read;
getStrings()no longer spends most of a read in garbage collection under G1
- Filter predicates
- One literal rule for every predicate: a literal must be a value the column's accessors return, adding
byte[],LocalDateTime,PqIntervalandInstantonINT96columns, andinfor every literal type butboolean isNullandisNotNullon struct,LISTandMAPgroups- A binary predicate is evaluated once per dictionary entry instead of once per row
- A filter-only column is not read in row groups whose statistics prove every row matches, and row groups and pages that hold only
NaNare skipped inandnotInin the parquet-java compatibilityFilterApi
- One literal rule for every predicate: a literal must be a value the column's accessors return, adding
- A reworked exception model:
IOExceptionsignals a transport failure only, a corrupt file raises the uncheckedParquetReadException, and read failures name the row group and column they occurred in TIMESTAMPcolumns overFIXED_LEN_BYTE_ARRAY(12), spanning the years 0001 to 9999 at nanosecond precision, are read, filtered and written- Correctness fixes
- Filters, including parquet-java filters through the compatibility layer, no longer return wrong rows for
NaNvalues, unsigned integers, inverted min/max bounds, statistics in an unknown sort order, and byte literals onDECIMALandFLOAT16columns INTERVALcolumns written by parquet-java are read asINTERVALinstead of asNULLcolumns- A column whose name contains a dot can be projected
byteRangeon a multi-file reader applies to every file, not the first one only- A statistics value larger than 1 KB no longer fails the read
- S3 requests to an endpoint that names its default port no longer fail with
SignatureDoesNotMatch - Metadata lacking a required field raises
ParquetReadExceptioninstead of being read with default values
- Filters, including parquet-java filters through the compatibility layer, no longer return wrong rows for
- CLI
divejumps to a given row or row group, andprintandconvertselect the same rows with--skipand--row-groupprint,convert,inspectanddiverender values of a logical type identically;convert --format jsonwrites nested values as JSON objects and arraysschema -F AVROand-F PROTOemit schemas that Avro andprotocaccept- The native binary reads
ZSTD-compressed files
Breaking Changes:
- Index-based accessors on
ColumnReaders,RowReaderandPqStructfollow the orderColumnProjection.columns(...)names the columns in, a nested field is selected by its full path only, andgetProjectedColumnNames()returns aList(#1066) - A corrupt file raises
ParquetReadException(unchecked;SchemaIncompatibleExceptionextends it) instead ofIOException, and the reader's build, iteration and close methods declareIOExceptionfor transport failures; see Error Handling for every condition and its type (#1104) - A filter literal must be a value the column's accessors return, such as a
Stringfor text columns only and anInstantfor UTC timestamps only; other literals throwIllegalArgumentExceptionwhen the predicate is resolved.FilterPredicate.SignedBinaryColumnPredicateis removed, andinStringsis deprecated in favour ofin(String, String...)(#1198, #1190) getStringthrowsIllegalArgumentExceptionon a column that does not hold text (#1196)LogicalType.DecimalTypetakes its precision before its scale; the static factories such asLogicalType.decimal(18, 2)are the documented way to construct a logical type (#1074)ColumnReader.getBinaryOffsets()is replaced bygetBinaryStarts()andgetBinaryEnds(), and a batch ends at every row-group boundary when the read includes a binary column (#1416, #513)- A
ColumnReaderobtained fromColumnReadersadvances only throughColumnReaders.nextBatch()(#1336) InputFile.of(ByteBuffer)andofBuffers(...)read a buffer from its position to its limit, so a buffer filled withput(...)must be flipped first (#1343)convert --format jsonwrites nested values as JSON objects and arrays instead of strings, andconvert --format csvwrites a list or map cell as JSON text (#1021)- The
dev.hardwood.RowGroupFilterJFR event counts row groups dropped by statistics only; Bloom filter and dictionary drops have their own events (#735, #1259)
See the 1.1.0.Beta2 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Arnab Nandy, Chandan Dhamande, Doug Hoard, Fawzi Essam, Fhatuwani Sikhwari, Gunnar Morling, Kohinoor Gupta, Mingjie Zhao, Mohamed Ibrahim Elsawy, Movindu Jayathilake, Shril Kumar.
1.1.0.Beta1 (2026-08-31)¶
Announcement blog post · API changes
Highlights of this release:
- Parquet files can be written —
RowWritera record at a time,ColumnWriterin aligned batches of primitive arrays — over the full type system, nested structs, lists and maps included- Every encoding and compression codec the format defines, bar the deprecated
LZ4framing andLZO - Statistics and
nan_countare written, page indexes andSizeStatisticsare not - Footer key-value metadata and the
created_byidentifier are set onParquetFileWriter, at any point untilclose()writes the footer
- Every encoding and compression codec the format defines, bar the deprecated
- Two push-down sources for skipping non-matching row groups
- A column's Bloom filter, for
eqandinpredicates - The chunk's dictionary page, when its encoding stats show every data page is dictionary-encoded
- A column's Bloom filter, for
- Further improved read performance
- A fast path for clean fixed-length
LISTpages - Bulk-unpacked
DELTA_BINARY_PACKEDminiblocks - Dictionary indices of 9 to 32 bits read a word at a time
- All-present definition levels are no longer materialized
- A fast path for clean fixed-length
- Multi-file reading
- Columns are matched by field path, so files may declare them in any order; a real mismatch raises
SchemaIncompatibleExceptionas the file opens - Physical
skip(N)is a true global offset across the concatenated files - Per-file metadata through
ParquetFileReader.getFileCount()andgetFileMetaData(int)
- Columns are matched by field path, so files may declare them in any order; a real mismatch raises
- Correctness fixes
- A
FilterPredicatenaming a group resolved to one of its leaves and returned wrong rows; a group name is now rejected withIllegalArgumentExceptionat reader creation - Batch sizing accounts for list fan-out, so a high-fan-out repeated column no longer drains a whole file into a single batch and exhausts the heap
GeographyType.algorithmis read as the enum it is, so a non-spherical geography column no longer reads asSPHERICAL; an unrecognized algorithm reportsEdgeInterpolationAlgorithm.UNKNOWN- Variant decoding bounds-checks lengths, guards 32-bit offset arithmetic, corrects the metadata offset-size read, and limits nesting depth
- A
- The
hardwoodCLI moves from picocli/Quarkus to aesh, which makes it start fasterdivenavigation is unified across all screens: every key moves the cursor,▶marks whatEnteracts onconvertpreserves types and NULL — JSON writes real scalars andnull, CSV an empty field, with--null-stringto overrideinfosurfaces the file's key-value metadata
Breaking Changes:
Validitymoves todev.hardwood, andColumnReader's rawgetDefinitionLevels()/getRepetitionLevels()give way to the layer model (#9, #749)ColumnIndex.nullPages()andnullCounts()returnboolean[]andlong[], and the canonical constructors ofColumnChunk,ColumnIndex,ColumnMetaDataandOffsetIndextake further components — reading these records is unaffected, constructing them directly is not (#607, #856, #903)S3InputFile.length()declaresthrows IOException, matching theInputFilecontract (#1072)hardwood help <command>is removed — usehardwood <command> --help(#686)
See the 1.1.0.Beta1 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Arnab Nandy, Chandan Dhamande, Fawzi Essam, Florian Meyer, Gunnar Morling, Hursh, Hyungun, Joshua Buss, Karen Barseghyan, Kohinoor Gupta, Mehmet Turac, Mingjie Zhao, Morax, Nikulin Nikita, Rion Williams, Sebastian Legarraga, Semyon Sinchenko, Shaik Sameer, Shril Kumar, Ståle Pedersen.
1.0.0.Final (2026-06-25)¶
Announcement blog post · API changes
Highlights of this release:
- Float and double row group and page pruning honors the file's column order, with
ColumnOrdersurfaced on the API;ResolvedPredicatefloat/double convenience constructors are now public - Legacy list encodings from older writers — un-annotated repeated fields and 2-level lists — are recognized as lists
- MAP columns without a value field (key-only
key_valuegroups) are read instead of throwing - Sub-field projections into MAP values and VARIANT groups pull in the structural columns they require (the MAP's key, every VARIANT leaf)
AvroRowReaderhonors column projections, and DECIMAL, UUID, UINT_32, and FIXED columns now read correctly- The multi-file
Hardwoodentry point accepts a caller-suppliedHardwoodContext, for control over decoder thread-pool sizing and sharing a context across readers ColumnReaderandRowReaderclose()methods are now idempotent, fixing a close-time performance regression from Beta2- Logical types render as Parquet-style annotation tokens (e.g.
STRING) hardwood divefails fast with a clear message when stdout is not an interactive terminalhardwood printshort option names follow the conventional single-dash, single-character form (-s,-w,-i,-d);--transposeis long-only
See the 1.0.0.Final milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Fawzi Essam, Gunnar Morling, Leo Chashnikov, Mohamed Ibrahim Elsawy, Rion Williams, Yash Priyadarshan.
1.0.0.CR2 (2026-06-07)¶
Highlights of this release:
- Breaking:
RowReaderBuilder.firstRow()renamed toskip(), which now composes with a filter as a logicalOFFSET— rows are skipped after the filter is applied - Docker distribution of the
hardwoodCLI, published as a multi-arch image to GHCR - Configurable read batch size for
ColumnReader/ColumnReaders, with the default now sized adaptively from the projected column widths instead of a fixed record count - TIMESTAMP accessors honor
isAdjustedToUTC, with dedicated accessors for local (non-UTC) timestamps - Stricter metadata validation: negative sizes, counts, and offsets are rejected, as are shredded Variant objects repeating a field across
typed_valueandvalue - NaN-safe row group and page pruning for
floatanddoublecolumns - Duplicate map keys resolve to the last value per the Parquet spec
head()with a filter caps matched rows rather than scanned rows- Legacy MAP columns from older parquet-mr / Hive / Impala writers (which annotate only the inner
key_valuegroup) are now recognized as maps - API change reports are now published alongside the JavaDoc on the website
See the 1.0.0.CR2 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Alexei Zenin, Fawzi Essam, Gunnar Morling, Mohamed Ibrahim Elsawy.
1.0.0.CR1 (2026-05-31)¶
Announcement blog post · API changes
Highlights of this release:
- Breaking:
ColumnReaderrebuilt around a layer model, with per-layer validity, offsets, and real-item-only sizing for nested data (see the Layer Model docs);ColumnReaderis now marked@Experimental - More performant evaluation of multi-column filter expressions
- Split-aware reading via
RowGroupPredicate.byteRange(...), for Hadoop-style split integrations - Coordinated multi-column reads via
ColumnReaders.nextBatch()/getRecordCount() - Richer
RowReadervalue model: by-index field access onPqStruct, key-based lookup and typed accessors onPqMap, typedListaccessors onPqList, and additional variant accessors - Float16 logical type support (readable values and filter predicates) and recognition of the
NullTypelogical annotation - First-cut geospatial support (GEOMETRY/GEOGRAPHY logical types and bounding-box metadata)
- Reading of local files larger than 2 GB
- CLI: exhaustive logical-type formatting;
hardwood dive: faster navigation of large collections and corrected "go to latest" in the data preview
See the 1.0.0.CR1 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Carlos Sousa, Fawzi Essam, Gunnar Morling, Manish, Mohamed Ibrahim Elsawy, muhannd Sayed, polo, Prashant Khanal, Rion Williams, Said Boudjelda.
1.0.0.Beta2 (2026-04-29)¶
Announcement blog post · API changes
Highlights of this release:
- Interactive
hardwood diveTUI for exploring Parquet files - Parquet Variant logical type, including shredded reassembly
- Additional logical types: INTERVAL, MAP/LIST, INT96 timestamps
- Faster reads via a parallel per-column pipeline and per-column in-page row skipping
- Reduced S3 traffic via byte-range caching, coalesced GETs, and small-column fetches
- Unified reader API based on builders
- CLI with reorganized
inspectsubcommands
See the 1.0.0.Beta2 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: André Rouél, Brandon Brown, Bruno Borges, Fawzi Essam, Gunnar Morling, Manish, polo, Rion Williams, Sabarish Rajamohan, Trevin Chow.
1.0.0.Beta1 (2026-04-02)¶
Announcement blog post · API changes
Highlights of this release:
- S3 and remote object store support with coalesced reads
- CLI tool for inspecting and querying Parquet files
- Avro
GenericRecordsupport via thehardwood-avromodule - Row group filtering with predicate push-down and page-level column index filtering
InputFileabstraction for pluggable file sources- S3 support and filtering in the parquet-java compatibility layer
- Project documentation site
See the 1.0.0.Beta1 milestone on GitHub for the full list of resolved issues.
Thank you to all contributors to this release: Arnav Balyan, Brandon Brown, Gunnar Morling, Manish, Nicolas Grondin, Rion Williams, Romain Manni-Bucau, Said Boudjelda.
1.0.0.Alpha1 (2026-02-26)¶
Highlights of this release:
- Zero-dependency Parquet file reader for Java
- Row-oriented and columnar read APIs
- Support for flat and nested schemas (lists, maps, structs)
- All standard encodings (RLE, DELTA_BINARY_PACKED, DELTA_BYTE_ARRAY, BYTE_STREAM_SPLIT, etc.)
- Compression: Snappy, ZSTD, LZ4, GZIP, Brotli
- Projection push-down, parallel page pre-fetching, and memory-mapped file I/O
- Multi-file reader and
parquet-javacompatibility layer - Optional Vector API acceleration on Java 22+
- JFR events for observability
- BOM for dependency management
Thank you to all contributors to this release: Andres Almiray, Gunnar Morling, Rion Williams.