[GH-3311] Make RS_SetBandNoDataValue honor NULL to remove a band's no-data value in Spark SQL - #3312
Open
james-willis wants to merge 3 commits into
Open
Conversation
…e in Spark SQL The docs and the common Java implementation say a null noDataValue removes the band's no-data value, but the catalyst expression null-propagated on any null argument, so RS_SetBandNoDataValue(raster, 1, NULL) returned a null raster and the documented clear path was unreachable from SQL. Route the 2- and 3-arg overloads through the allowRightNull pattern (adding an arity-3 variant) so a null noDataValue reaches the Java implementation. The 4-arg replace overload still null-propagates since replacing pixels with a null no-data value is meaningless.
james-willis
force-pushed
the
fix/rs-setbandnodatavalue-null-clear
branch
from
September 2, 2026 17:26
3768356 to
ab5f6a3
Compare
james-willis
marked this pull request as ready for review
September 2, 2026 17:26
jiayuasu
reviewed
Sep 3, 2026
jiayuasu
reviewed
Sep 3, 2026
jiayuasu
reviewed
Sep 3, 2026
jiayuasu
reviewed
Sep 3, 2026
1 task
jiayuasu
reviewed
Sep 5, 2026
The null path consulted band 1's no-data value to decide whether there was anything to remove, so clearing band N was a no-op whenever band 1 had none. It went unnoticed because the path was unreachable from SQL and the Java tests only ever cleared band 1.
GeoTiffWriter writes a default GDAL_NODATA of 0 when a coverage has no no-data value, so writing and re-reading a raster invented one: a raster that never had a no-data value came back reporting 0. That also made a cleared no-data value reappear on the round trip. Set WRITE_NODATA to false in that case only, leaving an explicit -Dgeotiff.writenodata=false untouched.
james-willis
force-pushed
the
fix/rs-setbandnodatavalue-null-clear
branch
from
September 8, 2026 23:39
b2070c0 to
e30f176
Compare
Collaborator
Author
|
I simplified this PR. The issues with bandwise no data values, GC_NODATA persisting after SetNoDataValueCalls, and writing geotiff No Data Values are orthogonal to this PR and thus out of scope. They are best handled separately. |
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.
Did you read the Contributor Guide?
Is this PR related to a ticket?
[GH-XXX] my subject. Closes RS_SetBandNoDataValue: documented "NULL removes the no-data value" behavior is unreachable from Spark SQL #3311What changes were proposed in this PR?
Scoped down to just making a
NULLnoDataValuebehave as documented. TheGC_NODATAproperty work that grew out of the review is split into a follow-up, since it turned out to be a separate problem with its own failure modes (see the end of this description).Make the documented NULL behavior reachable from SQL. The docs and the common Java implementation both say a
nullnoDataValueremoves the band's no-data value, but from Spark SQL that path was unreachable: the catalyst expression wrappedRasterBandEditors.setBandNoDataValuewithinferrableFunction2/3/4, which null-propagate as soon as any argument is null, soRS_SetBandNoDataValue(raster, 1, NULL)returned a NULL raster instead. This matches PostGISST_SetBandNoDataValueNULL-clears semantics and the behavior SedonaDB adopted in apache/sedona-db#1158.Added
InferrableFunction.allowRightNull3, an arity-3 counterpart of the existingallowRightNullpattern (allows the third argument to be null; still null-propagates when the raster or band index is null), and wired the 2- and 3-argument overloads through it. It is a separate name rather than an overload because overloadingallowRightNullbreaks eta-expansion of the overloaded functions passed to it at existing call sites. The 4-argumentreplaceoverload still null-propagates, since replacing pixels with a null no-data value is meaningless; the docs now say so.Clear the target band, not band 1. The null path consulted band 1's no-data value to decide whether there was anything to remove, so clearing band N was a no-op whenever band 1 had none. Latent until now because the path was unreachable from SQL and the Java tests only ever cleared band 1.
Stop
RS_AsGeoTiffinventing a no-data value.GeoTiffWriterwrites a defaultGDAL_NODATAof 0 when a coverage has no no-data value, so a raster that never had one came back from a write/read round trip reporting0. This is independent of NULL handling, but it is what made a cleared no-data value reappear on the round trip, so it is fixed here:WRITE_NODATAis set to false in that case only, leaving an explicit-Dgeotiff.writenodata=falseuntouched.How was this patch tested?
The existing
RS_SetBandNoDataValuetest could not distinguish clearing from null-propagation —RS_BandNoDataValuereturns null for a null raster too, so it passed under both semantics. It now sets a no-data value, clears it with NULL through both the 2- and 3-argument forms, and asserts the resulting raster is non-null whileRS_BandNoDataValueon it is null; a NULL raster input still yields NULL.New coverage for the other two fixes: clearing band 2 of a raster whose band 1 has no no-data value (both at the Java and SQL levels), a SQL round trip asserting a cleared value does not come back through
RS_AsGeoTiff, and twoRasterOutputTestcases pinning that a raster without a no-data value round-trips without gaining one while a raster with one keeps it.Each new assertion fails without its fix. Full runs:
common1339 passed,rasteralgebraTest168 passed,rasterIOTest21 passed.Did this PR include necessary documentation updates?
Follow-up, not in this PR
GC_NODATAis a single coverage-wide property while no-data is declared per band, and the two are not kept in sync in either direction. This PR deliberately does not touch that:GC_NODATAthe declared value is then ignored by anything reading the property —RS_MapAlgebrakeeps using the old sentinel. On a raster without one, nothing creates it, so a freshly set no-data value has no effect onRS_MapAlgebraat all.GDAL_NODATAslot, and GDAL itself spills a divergent per-band value into a.aux.xmlsidecar rather than the TIFF, whichRS_AsGeoTiffhas no equivalent of since it returns bytes.Keeping the property in step with the sample dimensions in both directions, and deciding what
RS_AsGeoTiffshould do with a heterogeneous state, belongs in a separate PR.