Commit 7dc03c6
fix(compiler): preserve nullability when casting JSON arrow expressions (#3792)
PostgreSQL's JSON accessor operators (-->, ->>, #>, #>>) return SQL
NULL when the requested key or path is missing. Wrapping such an
expression in a type cast does not make the result non-nullable.
Today sqlc emits a non-nullable Go `string` for queries like:
SELECT id,
(data ->> 'PhoneNumber')::text AS phone_number,
(data ->> 'ContactName')::text AS contact_name
FROM jobs;
which crashes any consumer scanning into pgtype.Text / *string / sql.NullString
when the key happens to be absent. Real Postgres returns NULL in that
case; the generated row should too.
Fix:
* internal/sql/lang/operator.go — new IsJSONNullableOperator(s)
classifier covering ->, ->>, #>, #>>.
* internal/compiler/output_columns.go — in the TypeCast case, when the
wrapped argument is an A_Expr whose operator IsJSONNullableOperator,
set col.NotNull = false. Tight scope: only the immediate cast arg.
All other cast paths (literal, column from NOT NULL source, etc.)
are unchanged — confirmed via regression repros.
Tests:
* internal/sql/lang/operator_test.go — TestIsJSONNullableOperator
positive + negative table-driven test.
* internal/endtoend/testdata/json_arrow_cast_3792/postgresql/pgx/v5/ —
end-to-end fixture reproducing the issue under pgx/v5; golden files
show pgtype.Text for the three ->>-derived columns.
Verified locally:
* Repro from the issue now generates pgtype.Text instead of string.
* ('foo')::text, (column_not_null)::text still emit string. NULL
literal cast still emits pgtype.Text (existing behavior preserved).
* go test ./internal/sql/... ./internal/compiler/... ./internal/engine/...
passes; gofmt -l clean on touched files; go vet clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent a3b0cfd commit 7dc03c6
9 files changed
Lines changed: 167 additions & 0 deletions
File tree
- internal
- compiler
- endtoend/testdata/json_arrow_cast_3792/postgresql/pgx/v5
- go
- sql/lang
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
369 | 381 | | |
370 | 382 | | |
371 | 383 | | |
| |||
Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 55 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
0 commit comments