Repository navigation
fix(postgres): tell a self-join's alias.* apart by its alias - #92
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
ℹ️ No critical issues. One edge case is inline: the check that each target is one column is gone.
Reviewed changes
I reviewed the whole diff. It teaches aliased_columns to expand a top-level alias.* over a table reading, so that each half of a self-join keeps its own nullability.
alias.*expansion: a star over an alias whose FROM item is a table now addswidth(table)entries of(table, nulled). A bare*, a star over a subselect, function or WITH item, and a table whose width is unknown or ambiguous still give nothing.width: this is the column count of the oneself.relationsentry with that name.RELATION_COLUMNS_QUERYfills it with attnum > 0, non-dropped,pg_table_is_visiblecolumns, which matches whata.*expands to for a visible table.- Count guard moved: the early
target_list.len() != countcheck is replaced byout.len() != countafter expansion. - Oracle and artifact:
whole_row_json_self_join's preserved side is no longer nullable. Three newinference_precisioncases cover a full self-join,a.*on the preserved side andb.*on the nulled side.engine.wasmis rebuilt.postgres.mdx:444already states the rule ("told apart by its alias"), so the docs need no change.
claude-opus-5-5 | 𝕏
53aabcd to
19e830d
Compare
There was a problem hiding this comment.
✅ No new issues found. The edge case from the last review is closed.
Reviewed changes
Since the last review, one commit adds two guards to aliased_columns, so a misjudged alias.* width can no longer line up with the wrong columns.
(x).*gives up: a target that is anA_IndirectioncontainingA_Starnow returns no readings at all. Every target other thanalias.*is therefore exactly one column, and the finalout.len() != countcheck catches a wrong width on its own.- Schema-named stars give up:
itemsnow collects intoqualifiedthe alias of eachRangeVarwritten with a schema, and a star over one of those aliases returns no readings.widthonly ever answers for the visible table, so this keeps it from reporting the visible table's width for a different table of the same name. - Unit tests: the counter-example from the last review, a variant without the composite and a lone
(b.addr).*are now asserts ina_star_of_a_reading_is_its_tables_columns_by_that_reading.
claude-opus-5-5 | 𝕏

In a table joined to itself,
alias.*lost track of which reading each column came from. So every column of the table came out nullable, including on the preserved side. The engine tells a self-join's readings apart by alias foralias.columntargets, but it gave up as soon as a*appeared in the select list. Ato_jsonb(a)is read through ana.*probe, so it was affected too:earlier: { id: int4?, name: text?, email: text? }?earlier: { id: int4, name: text, email: text? }, withlaterunchanged as{ id: int4, name: text, email: text? }?The rule
alias.*over a table's reading stands for that table's columns, in order, by that reading. Each column is nullable where an outer join nulls that reading, and otherwise it is as the catalog says. A bare*, a star of an item that isn't a table, and a column count that doesn't add up still say nothing, as before.Oracle (
inference_precision)whole_row_json_self_join:earliergoes from{ id: int4?, name: text?, email: text? }?to{ id: int4, name: text, email: text? }.whole_row_json_self_full_join: both objects stay nullable, and their fields keep their own nullability.whole_row_star_self_join(a.*, b.name):{ id: int4, name: text, email: text?, later: text? }.whole_row_star_self_join_nulled(a.name, b.*):{ earlier: text, id: int4?, name: text?, email: text? }. The nulled side stays nullable.