We were unable to construct the commit group for this pull request: None of the pull request's commits have been successfully processed as yet.

Do not use Parquet bloom or dictionary filters when the requested type changes what a value hashes to #121077

Open
groeneai opened 5:55pm on September 19, 2026 wants to merge 0 commits into clickhouse/clickhouse master from
parquet-hash-filter-output-type-value-space

Pull Request Overview

  • Opened on September 19, 2026
  • Status Open
  • Commit count 0

Total Delta

0 Total Diff Delta

Open Days

Open 0 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Data pending calculation for pull request

Author avatar

Do not use Parquet bloom or dictionary filters when the requested type changes what a value hashes to

Related: https://github.com/ClickHouse/ClickHouse/issues/118376
Related: https://github.com/ClickHouse/ClickHouse/pull/121033

Changelog category (leave one):


  • Bug Fix (user-visible misbehavior in an official stable release)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fixed wrong results when reading a Parquet file with a structure hint whose type differs from the stored one. Bloom-filter and dictionary-filter row-group pruning hashed the query constant in the requested type but the stored values in the Parquet physical type, so a row group holding a match was skipped and count() could return 0 instead of the real count. Affected hints include narrowing (UInt32 read as UInt16), a signedness flip below the physical width (UInt8 read as Int8), Enum8/Enum16, Date/Date32 over a column wider than 16 bits, and String/FixedString(N) length mismatches.

Description

Same family as #118376; its min/max-statistics leg is #121033, whose description pre-committed this follow-up.

INSERT INTO FUNCTION file('u32.parquet', Parquet, 'x UInt32') SELECT toUInt32(70000);

SELECT count() FROM file('u32.parquet', Parquet, 'x UInt16') WHERE x = 4464; -- 0, should be 1

Root cause. The two hashed sides live in different value spaces. The stored side is hashed in physical space: the dictionary is materialized as decoded_type and cast to the Parquet physical type by parquetTryHashColumn, and the file's bloom filter holds digests of physical values. The query side is a KeyCondition constant of the requested type. Nothing checked that the conversion between them preserves static_cast<physical>, which the hints listed above each break. The min/max legs are gated by allow_stats and can cast endpoints into the output type; the hash legs were given neither.

Change. One predicate, parquetHashFilterOutputTypeIsExact, checked once per column in Reader::prepareBloomFilterCondition. A column that fails it is never made eligible, so both legs stay off.

I withhold rather than reconcile: mapping a requested-type constant back into the physical space the digests live in is one-to-many exactly in the broken cases (65536 physical values per UInt16), so the bloom leg cannot be reconciled at all.

Pruning is kept wherever the two spaces agree. It is given up wherever soundness came from the data rather than the types, for example a signed column read into an unsigned space (Int32 as DateTime) or a String column read as FixedString(N).

Validation. New test 05231_parquet_hash_filter_output_type_value_space: 16 arms that returned the wrong count, 30 controls asserting via read_rows that each leg still skips three of four row groups, and two no-pruning baselines so no control passes vacuously. The parquet suite ran on both binaries, failure set unchanged.


Workflow [PR]
Sync PR [sync-upstream/pr/121077]
<!-- CI automatic block end :ci_links: -->

Comments Threads Pending Resolution

Resolved Comment Threads

clickhouse-gh[bot] reviewed on September 19, 2026