postgres: infer RETURNING OLD/NEW columns as nullable - #4360
Open
DoTuanAnh2k1 wants to merge 1 commit into
Open
Conversation
A `RETURNING` clause can reference the OLD/NEW transition relations (Postgres 18+). Those columns are nullable even when the underlying column is NOT NULL: OLD is null for a row added by INSERT (including `ON CONFLICT DO NOTHING`) and NEW is null for one removed by DELETE. The EXPLAIN-based nullability inference didn't account for this and reported such columns as non-nullable, causing an unexpected-null error at runtime with the query macros. Fixes transact-rs#4332.
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.
Why
sqlx::query!infers a non-nullable column for aRETURNING old.<col>clause even when the value is null at runtime, producing an unexpected-null error.A
RETURNINGclause can reference the OLD/NEW transition relations (Postgres 18+). Those columns are nullable regardless of the underlying column'sNOT NULLconstraint:OLDis null for a row added byINSERT(includingON CONFLICT DO NOTHING),NEWis null for a row removed byDELETE.The EXPLAIN-based nullability inference only marked outer-join columns as nullable, so
RETURNING old.hashon aNOT NULL PRIMARY KEYwas reported as non-nullable (see #4332).What changed
visit_plannow marks anyModifyTableoutput referencing anold./new.transition relation as nullable, mirroring the existing over-approximation used for outer joins (a false positive here is safe; a false negative causes the runtime error). This required parsing theNode Typefield from the EXPLAIN plan.Testing
Added a unit test built from the exact
EXPLAIN (VERBOSE, FORMAT JSON)output in #4332; it fails on the current code ([None]) and passes with the fix ([Some(true)]).cargo test -p sqlx-postgres,cargo fmt --checkandcargo clippy -p sqlx-postgresare clean.Fixes #4332.
Disclosure: prepared with AI assistance.