Skip to content

fix(compiler): reject sqlc.embed on the nullable side of an outer join - #4679

Open
meem3443 wants to merge 1 commit into
sqlc-dev:mainfrom
meem3443:fix/embed-left-join-error
Open

meem3443 wants to merge 1 commit into
sqlc-dev:mainfrom
meem3443:fix/embed-left-join-error

Conversation

@meem3443

@meem3443 meem3443 commented Nov 6, 2026

Copy link
Copy Markdown

Updates #2997.

sqlc.embed(t) on the nullable side of an outer join generates code that compiles but fails at runtime when the row is missing (converting NULL to string is unsupported). The embedded struct is the table's model, so its fields aren't nullable.

Plain columns are already made nullable by isTableRequired, but embeds skipped it because their column has no Table. generate now fails for this case instead:

query.sql:2:23: sqlc.embed(u) on the nullable side of an outer join is not supported

The join rules are the same as for plain columns (LEFT, RIGHT, FULL). Embed.Param() is new so a self join like users a LEFT JOIN users b can be told apart by alias.

This only turns the runtime failure into a generate-time error, as #2858 did. A real fix needs a design choice (#2348). It is a breaking change for queries that work today because they never hit a missing row.

Limits:

  • Only the top-level FROM is checked, like plain columns, so embeds inside subqueries or CTEs are not covered.
  • It only applies to the default analyzer. With a database analyzer (database: in the config) inference errors are discarded by design, so the query still generates as before. The error cases run in the base context only for that reason.
  • The analysis core (SQLC_TEST_CORE) doesn't support embeds yet.

Test

New end-to-end cases internal/endtoend/testdata/sqlc_embed_outer_join_*: left (SQLite, PostgreSQL), right and self expect the error, ok still generates. The four error cases fail without the change.

An embedded struct is the model of its table, whose columns may not be
nullable. When the table is on the nullable side of a LEFT, RIGHT or FULL
JOIN the query returns NULLs the struct cannot hold, so the generated code
compiled but failed at runtime with "converting NULL to string is
unsupported".

Report an error at generate time instead. The join walk is the one that
already makes plain columns nullable (isTableRequired); embeds skipped it
because their column carries no table. Only the top-level FROM clause is
checked, as for plain columns.

The error is returned together with the columns. A database analyzer
discards inference errors and replaces the columns when none come back,
which would have turned the embed into flat columns.

Updates sqlc-dev#2997
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant