Skip to content

fix(postgres): type an aggregate beside a column in built json - #45

Merged
colinhacks merged 1 commit into
mainfrom
json-aggregate-probe
Oct 23, 2026
Merged

colinhacks merged 1 commit into
mainfrom
json-aggregate-probe

Conversation

@colinhacks

Copy link
Copy Markdown
Owner

On Postgres, JSON a statement builds is typed by describing a probe that selects the constructor's values over the statement's FROM. The probe had no GROUP BY, so an aggregate beside one of the statement's own columns made it fail, and every field of the object came out unknown:

select jsonb_build_object('name', u.name, 'posts', count(p.id)) as author
from users u left join posts p on p.user_id = u.id
group by u.id

was { author: { name: unknown; posts: unknown } } and is now { author: { name: string; posts: number } }.

In a statement that groups or aggregates, every expression of its select list outside an aggregate's arguments is valid under its GROUP BY. Now those expressions go in a probe that carries it, while the statement's bare columns and the arguments of its aggregates (what jsonb_agg(…) collects) stay in the probe without one. That covers aggregates the engine has no rule for too, such as array_agg or a user's own. The probe under the GROUP BY also settles each aggregate's nullability the way the statement's own select list would: max(p.title) over an inner join is non-null, and sum of a nullable column stays nullable. A statement that neither groups nor aggregates keeps its one probe. A GROUP BY by position names the probe's own columns, so it is not carried, and such an object stays unknown as before.

Tests:

  • oracle/cases/json_grouped_values: 12 statements (counts, sums, max over inner and outer joins, array_agg, an expression beside an aggregate, an aggregating statement without GROUP BY, GROUP BY by position), sound over the seeded edge values.
  • A unit test of the probe texts.
  • Run locally: the Rust build and tests, the artifact fingerprint, the oracle with --strict-coverage, the loader's typecheck and tests, and the package build.

@vercel

vercel Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 23, 2026 12:23am UTC

Request Review

Copilot AI balanced review requested due to automatic review settings October 22, 2026 22:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

A GROUP BY that names an output column is copied as-is into the new grouped probe. That probe has no such column, so it fails, and fields main typed now come out unknown.

Reviewed changes

I reviewed the split of a grouping or aggregating statement's top JSON probe into a probe that carries the GROUP BY and one that does not.

  • Two probes for the top context: non-column expressions outside an aggregate's arguments now go in a probe written with the statement's GROUP BY (Draft.grouped, outside_aggregates). Bare columns and jsonb_agg(…) arguments stay in a probe without it.
  • Ungrouped aggregating statements: aggregating/constructs_aggregate find these. They get an empty group, so their aggregates are probed apart from the aggregated arguments.
  • GROUP BY by position: this is detected by an AConst item and falls back to the old single probe.
  • Tests and docs: there is a new json_grouped_values oracle case with 12 statements, a unit test of the probe texts, a docs paragraph, and a rebuilt engine.wasm.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread engine/postgres/src/jsonshape.rs Resolved
@colinhacks
colinhacks force-pushed the json-aggregate-probe branch from 7ebd0bb to c0dfb4c Compare October 22, 2026 23:40

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The output-name GROUP BY regression is fixed, so a statement like that types no worse than on main.

Reviewed changes

I reviewed c0dfb4c, which keeps a GROUP BY that may name a select-list column out of the grouped JSON probe.

  • Added names_output: a GROUP BY item that is a position, or a bare name equal to a top target's alias or derived name (target_name), now sets group = None. That falls back to the single probe from before. A target that is a bare column has no derived name, so p.title … GROUP BY title still carries its GROUP BY, which the new unit assert confirms.
  • Extended coverage: there are two new oracle statements. group_by_alias.sql gives { n: int8, best: text? }, the same as before this PR. group_by_alias_beside.sql gives unknown? values, the same as the positional case. There are also three unit asserts for the probe texts.
  • Updated docs and setup comment: the postgres.mdx paragraph now names both forms and says the values are unknown, not the object.
  • Rebuilt packages/postgres/engine.wasm in the same commit.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks force-pushed the json-aggregate-probe branch from c0dfb4c to f1e1e1a Compare October 22, 2026 23:57
A JSON constructor's values were probed without the statement's GROUP
BY, so an aggregate beside one of its columns failed the probe and left
the whole object unknown. Expressions outside an aggregate's arguments
are probed under the GROUP BY now; bare columns and arguments are not.
A GROUP BY by position or by a name of the select list is not carried.
@colinhacks
colinhacks force-pushed the json-aggregate-probe branch from f1e1e1a to 2e90fa3 Compare October 23, 2026 00:22
@colinhacks
colinhacks merged commit f3c4f93 into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the json-aggregate-probe branch October 23, 2026 00:29
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.

2 participants