Skip to content

fix(parquet): honor explicit V1 FLBA dictionary opt-in - #10569

Open
subotac wants to merge 4 commits into
apache:mainfrom
subotac:fix/parquet-v1-flba-dictionary
Open

fix(parquet): honor explicit V1 FLBA dictionary opt-in#10569
subotac wants to merge 4 commits into
apache:mainfrom
subotac:fix/parquet-v1-flba-dictionary

Conversation

@subotac

@subotac subotac commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Parquet 1.0 writers silently ignored explicit dictionary settings for FIXED_LEN_BYTE_ARRAY columns, even though dictionary encoding is valid for the physical type.

What changes are included in this PR?

Honor explicit global and per-column dictionary opt-ins for Parquet 1.0 FLBA columns while preserving the existing default-off behavior. Update CDC expectations for FixedSizeBinary arrays when dictionary encoding is
explicitly enabled.

Are these changes tested?

Yes. Added coverage for the default behavior, global opt-in, per-column override, and explicit disable cases. The focused tests, full Parquet suite, Clippy, and formatting checks pass.

Are there any user-facing changes?

Callers can now explicitly enable dictionary encoding for Parquet 1.0 FLBA columns. The default behavior is unchanged.

Preserve the existing Parquet 1.0 default for fixed-length byte arrays,
but honor explicit global and per-column dictionary settings.

Update writer and CDC coverage for the newly reachable dictionary path.
@github-actions github-actions Bot added the parquet Changes to the parquet crate label Aug 6, 2026

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @subotac, nice job keeping the old behavior.

I do feel somewhat conflicted about this. I think the decision to not support FLBA dict encoding for 1.0 was a mistake from the get-go. Clearly by 2014 even parquet-mr was able to read this combination, so I highly doubt there are any readers still in use that cannot (as evidence arrow-cpp has no such restriction on using dict encoding with FLBA). If we drop maintaining the old behavior this PR can be made much simpler. I'll wait a bit and let others opine on the best way forward here before merging.

cc @alamb @Jefffrey @jhorstmann

@Jefffrey Jefffrey added the bug label Aug 7, 2026
@Jefffrey

Jefffrey commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

is this something we should raise as a question to parquet and/or parquet-java (formerly parquet-mr)? it does seem that this decision stemmed from parquet-mr (as linked in the issue thread, apache/parquet-java#30) and the implementation here followed that (sunchao/parquet-rs#196)

i was trying to check the parquet format at version 1.0.0 and was quite confused as my read was nothing prevents fixed len byte arrays from being dictionary encoded either 🤔

@etseidl

etseidl commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

is this something we should raise as a question to parquet and/or parquet-java (formerly parquet-mr)?

I think that's probably a good idea, given that I cannot find this behavior documented anywhere in the parquet-java API docs nor the spec. In the meantime, I think this PR does a nice job of threading the needle...it maintains the current behavior by default, but allows for users to explicitly enable dict encoding.

Before merging, I think the docs for the writer properties should be updated to explain the current state (i.e. by default, dictionary encoding will not be used if the writer version is 1.0, but users can explicitly enable dictionary encoding either globally or per column to override this default behavior). @subotac, would you be willing to add this documentation?

@subotac

subotac commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

I updated both dictionary-setting methods to document this explicitly: V1 FLBA remains default-off, while an explicit global or per-column true enables dictionary encoding.

@subotac
subotac requested a review from etseidl August 7, 2026 21:12

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @subotac, this looks good.

@Jefffrey

Copy link
Copy Markdown
Contributor

i'm kinda of the mind to just enable it by default, instead of honoring the previous default behaviour. for reference, was able to use codex to generate some duckdb code which shows that they can generate such files so i'm fairly sure its only a parquet-java limitation:

DuckDB v1.5.5 (Variegata)
Enter ".help" for usage hints.
memory D CREATE TABLE values_to_write AS
           SELECT
               CASE
                   WHEN i % 2 = 0
                       THEN UUID '00000000-0000-0000-0000-000000000001'
                   ELSE UUID '00000000-0000-0000-0000-000000000002'
               END AS value
           FROM range(200) AS rows(i);
memory D
memory D   COPY values_to_write
           TO 'duckdb-v1-flba-dictionary.parquet'
           (
               FORMAT PARQUET,
               PARQUET_VERSION V1,
               COMPRESSION UNCOMPRESSED
           );
memory D SELECT value, count(*)
           FROM read_parquet('duckdb-v1-flba-dictionary.parquet')
           GROUP BY value
           ORDER BY value;
┌──────────────────────────────────────┬──────────────┐
│                value                 │ count_star() │
│                 uuid                 │    int64     │
├──────────────────────────────────────┼──────────────┤
│ 00000000-0000-0000-0000-000000000001100 │
│ 00000000-0000-0000-0000-000000000002100 │
└──────────────────────────────────────┴──────────────┘
memory D   SELECT
               path_in_schema,
               type,
               encodings,
               dictionary_page_offset
           FROM parquet_metadata('duckdb-v1-flba-dictionary.parquet');
┌────────────────┬──────────────────────┬──────────────────┬────────────────────────┐
│ path_in_schema │         type         │    encodings     │ dictionary_page_offset │
│    varcharvarcharvarchar      │         int64          │
├────────────────┼──────────────────────┼──────────────────┼────────────────────────┤
│ value          │ FIXED_LEN_BYTE_ARRAY │ PLAIN_DICTIONARY │                      4 │
└────────────────┴──────────────────────┴──────────────────┴────────────────────────┘
memory D   SELECT format_version
           FROM parquet_file_metadata('duckdb-v1-flba-dictionary.parquet');
┌────────────────┐
│ format_version │
│     int64      │
├────────────────┤
│              1 │
└────────────────┘
memory D

the issue mentions

I found one outlier (parquet-cpp), but that is besides the point. The only conclusion to draw from this is that readers can read those files; I created a test table with pyiceberg and read it back with iceberg-rust and data is intact, no errors.

maybe if we double check against parquet-java, to see if it can read such files, we can just simplify this PR

@etseidl

etseidl commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

i'm kinda of the mind to just enable it by default, instead of honoring the previous default behaviour. for reference, was able to use codex to generate some duckdb code which shows that they can generate such files so i'm fairly sure its only a parquet-java limitation:

Yes, it seems to be an ancient parquet-mr holdover. I'm pretty sure java has been able to read this for quite some time.

maybe if we double check against parquet-java, to see if it can read such files, we can just simplify this PR

I used pyarrow to generate a file with an FLBA column dict encoded and parquet-java 1.14 reads it just fine. I'd prefer to align with parquet-cpp and just enable the combination without opt-in. But I'm fine with the current state of this PR as well.

@alamb

alamb commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

i'm kinda of the mind to just enable it by default, instead of honoring the previous default behaviour. for reference, was able to use codex to generate some duckdb code which shows that they can generate such files so i'm fairly sure its only a parquet-java limitation:

Yes, it seems to be an ancient parquet-mr holdover. I'm pretty sure java has been able to read this for quite some time.

maybe if we double check against parquet-java, to see if it can read such files, we can just simplify this PR

I used pyarrow to generate a file with an FLBA column dict encoded and parquet-java 1.14 reads it just fine. I'd prefer to align with parquet-cpp and just enable the combination without opt-in. But I'm fine with the current state of this PR as well.

@subotac are you able to implement @etseidl 's request to avoid the opt in ? This PR also appears to have some merge conflicts that need to be resolved before we can merge it

@subotac

subotac commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Merged current main to resolve the conflicts. \cargo test -p parquet --lib\ passes locally (1,380 tests). @etseidl, could you take another look once the checks finish?

@subotac
subotac requested a review from etseidl September 13, 2026 17:00

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting closer, thanks!

Comment on lines +1985 to +1988
// Preserve the PARQUET_1_0 default, but honor an explicit opt-in.
(Type::FIXED_LEN_BYTE_ARRAY, WriterVersion::PARQUET_1_0) => {
props.dictionary_enabled_setting(path) == Some(true)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Preserve the PARQUET_1_0 default, but honor an explicit opt-in.
(Type::FIXED_LEN_BYTE_ARRAY, WriterVersion::PARQUET_1_0) => {
props.dictionary_enabled_setting(path) == Some(true)
}

Thanks @subotac, but I think what @Jefffrey was suggesting (and I now concur) is to remove this behavior altogether. In other words, users would need to opt out of dictionary encoding explicitly, regardless of the parquet writer version.


#[test]
fn test_column_writer_default_encoding_support_fixed_len_byte_array() {
let default_props = WriterProperties::builder()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this test would just check that for both v1 and v2 the writer uses dict encoding. Or we could just remove this test since FLBA is no longer a special case.

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

Labels

bug parquet Changes to the parquet crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[parquet] set_dictionary_enabled(true) is silently ignored for FIXED_LEN_BYTE_ARRAY columns at the default writer version

4 participants