Repository navigation
fix: keep NULL-valued MAP entries when writing JSON - #26500
Open
shantanutheone wants to merge 1 commit into
Open
shantanutheone wants to merge 1 commit into
shantanutheone wants to merge 1 commit into
Conversation
The JSON sink used arrow-json's default explicit_nulls = false, which skips any MAP entry whose value is NULL. The key and the map's cardinality were lost on round trip. Enable explicit nulls when the schema contains a Map at any depth, for both JsonSerializer and plan_to_json. explicit_nulls is writer-wide, so other NULLs in those files are now written as null instead of omitted. Schemas without a Map are written as before. Add an upgrade guide note for 56.0.0. Closes apache#26493
Author
|
Opened #26500 for this. It turns on arrow-json's |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
COPY … STORED AS JSON(andINSERT INTOa JSON table) drops everyMAPentry whose value is NULL:{color: red, size: NULL}is written as{"color":"red"}#26493.Rationale for this change
COPY ... STORED AS JSONandINSERT INTOa JSON table drop every MAP entry whose value is NULL.map(['color','size'], ['red', NULL])is written as{"color":"red"}, so reading the file back gives a different map: thesizekey is gone andcardinalitydrops from 2 to 1. A map with only NULL values, such asmap(['flag'], [NULL]), comes back as{}.The JSON sink uses arrow-json's writer with its default
explicit_nulls = false, and arrow-json'sMapEncoderskips NULL-valued entries in that mode. For a struct field a missing key still reads back as NULL when the table has a schema, but for a map the entry is the data, so leaving it out loses it.What changes are included in this PR?
datafusion/datasource-json/src/file_format.rs: a smallndjson_writerhelper that builds the line-delimited writer withwith_explicit_nulls(true)when the schema contains a Map at any depth (inside List, LargeList, FixedSizeList, ListView, LargeListView, Struct, Dictionary or RunEndEncoded).JsonSerializeruses it, which covers bothCOPYandINSERT INTO.datafusion/datasource-json/src/source.rs:plan_to_jsonuses the same helper, so both write paths behave the same.56.0.0.mdfor the output change.Schemas without a Map are written exactly as before.
What is the testing strategy for this PR?
New round-trip cases at the end of
copy.slt: write tables with NULL-valued MAP entries to JSON, read them back with a declared schema, and check the values andcardinality. One case has the map at the top level, the other has it inside a struct and inside a list, so the nested check is covered. Both cases fail without the source change (the maps read back as{}or lose their NULL keys) and pass with it.Two small unit tests in
file_format.rscover what SQL round trips cannot see:contains_mapfinds a Map inside List/Struct, Dictionary and RunEndEncoded, andJsonSerializerwrites{"id":null,"m":{"k":null}}when a Map is present but still omits the NULL ({}) when there is none.Are there any user-facing changes?
Yes, for JSON files whose schema contains a Map. Explicit nulls are a writer-wide setting in arrow-json, so in those files every other NULL (top-level columns and struct fields) is now written as
"field":nullinstead of being omitted. Reading back with a declared schema gives the same values. With schema inference, a column or struct field that is NULL in every sampled row is now inferred as typeNullinstead of being missing. Files without a Map are unchanged. No public API changes.Not covered here: unbounded JSON stream tables (
datafusion/catalog/src/stream.rs) build their ownLineDelimitedWriter, so they still drop these entries, and the CLI's--format json/ndjsonoutput is unchanged.datafusion-catalogdoes not depend ondatasource-json, so sharing the helper means moving it (probably todatafusion-datasource). I can do that in a follow-up.Open question: whether maintainers would prefer a
format.explicit_nullsoption onJsonOptionsinstead of tying this to Map columns.