8000 fix(postgres): keep not null on columns read beside a set operation by colinhacks · Pull Request #4 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

fix(postgres): keep not null on columns read beside a set operation - #4

Merged
colinhacks merged 3 commits into
mainfrom
cte-union-nullability
Oct 22, 2026
Merged

colinhacks merged 3 commits into
mainfrom
cte-union-nullability

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

A set operation anywhere in a Postgres statement made the engine distrust the provenance of every column it returned, because the server reports a set operation's columns as the leftmost arm's. So a union inside a with query, a derived table or a where … in (subquery), and every with recursive query, left each column of the statement | null and dropped its CHECK list.

Now only a column that may come through the set operation is treated that way. A bare reference to a column of a table in the statement's own FROM keeps its NOT NULL and its CHECK list, whatever its with queries and subqueries hold, and a column read from a with query keeps the nullability that query's own arms give it, as it already did for a derived table.

Statement Before After
with a as (select p.id, p.title from posts p union all select c.id, c.body from comments c) select id, title from a id: int8?, title: text? id: int8, title: text
with recursive tree as (… union all …) select id, name, depth from tree id: int8?, name: text? id: int8, name: text
select u.name from users u where u.id in (select user_id from posts union select 1) name: text? name: text
with a as (select 1 as x union select 2) select e.kind from events e, a (kind checked in ('click', 'key')) kind: text? kind: 'click' | 'key'
with a as (select p.title as t from posts p union all select u.email from users u) select t from a (email nullable) t: text? t: text?

Columns read through a *, a join with an alias of its own, or a bare name among several FROM items stay as they were.

A recursive with query that holds a parameter also settles its columns' nullability by its arms now. A probe writes NULL where the statement has a placeholder, so the query's self-read inside a probe did not match the query being settled, and a counter such as t.depth + 1 in with recursive tree as (select …, 0 as depth from categories where id = :root union all select …, t.depth + 1 from categories c join tree t on …) stayed int4?; it is int4 now.

Tests:

  • oracle/cases/set_operation_provenance: 14 statements, the nullable-arm cases and a recursive counter among them, sound over the seeded edge values.
  • oracle/cases/timeouts/ancestors.sql: the recursive query's primary key is now non-null.
  • A unit test for the per-column reads (infer::target_reads).
  • 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.

Copilot AI balanced review requested due to automatic review settings October 22, 2026 16:04
@pullfrog
pullfrog Bot commented Oct 22, 2026 •
Copy link
Copy Markdown
Contributor

No configured credential can serve this run. Your Claude Pro/Max subscription was rejected. Sign in again by running pullfrog auth claude in a terminal.

The providers' responses
CLAUDE_CODE_OAUTH_TOKEN: 401: OAuth access token has been revoked.

Pick another model →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using claude-opus-5-5

@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 22, 2026 6:02pm UTC

Request Review

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.

@colinhacks
colinhacks force-pushed the cte-union-nullability branch from 05c81fd to 9fc930b Compare October 22, 2026 16:26
@pullfrog
pullfrog Bot commented Oct 22, 2026
Copy link
Copy Markdown
Contributor

No configured credential can serve this run. Your Claude Pro/Max subscription was rejected. Sign in again by running pullfrog auth claude in a terminal.

The providers' responses
CLAUDE_CODE_OAUTH_TOKEN: 401: OAuth access token has been revoked.

Pick another model →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using claude-opus-5-5

@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 critical issues. One minor soundness edge case is noted inline.

Reviewed changes

This is the first Pullfrog review on this PR, so it covers both commits.

  • Kept table provenance beside a set operation: a bare or qualified reference to a non-CTE table in the top-level FROM is now ItemRead::Table. own_provenance trusts only those columns when the statement holds a set operation, in both settle and checks.
  • Split target_reads from item_reads: per-target reads are now available without the "some query item is read" gate. Items now tracks Item::{Query, Table, Other} through a shared find.
  • Settled recursive with queries that hold a parameter: the with_stack self-read match now also compares jsonshape::without_placeholders forms, so a probe's NULL-for-$n text is recognized as the same query.
  • Added oracle coverage: added the set_operation_provenance case with 14 statements. timeouts/ancestors now types id as non-null.

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

Comment thread engine/postgres/src/postgres.rs Resolved
@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 star-alignment gap from the prior review is closed.

Reviewed changes

One commit landed since the prior Pullfrog review: efb3ae7.

  • Required at most one * before trusting positions in own_provenance: with a matching length and at most one *, that * has to be exactly one column wide. So each read lines up with its column, which mirrors the width rule in Engine::item_columns.
  • Added a unit test and an oracle case: own_provenance_lines_up_with_the_columns covers one * that is one column wide, a zero-width * that leaves the lengths unequal, and two stars. with_union_two_stars.sql pins { t: text?, t2: text?, name: text? }.

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

@colinhacks
colinhacks force-pushed the cte-union-nullability branch 2 times, most recently from 25842f4 to dffbe0d Compare October 22, 2026 17:50
A union anywhere in a statement, in a WITH query, a derived table or a
WHERE subquery included, made every column nullable and dropped its
CHECK list. Only a column read through the set operation is unsure of
its provenance now; a bare read of a table in the statement's own FROM
keeps its NOT NULL, and recursive WITH queries are typed by their arms.
Two `*` targets may be two columns and none, so as many targets as
columns no longer shows which column each target is.
@colinhacks
colinhacks force-pushed the cte-union-nullability branch from dffbe0d to 2ada6ba Compare October 22, 2026 18:01
@colinhacks
colinhacks merged commit 9fda42c into main Oct 22, 2026
4 checks passed
@colinhacks
colinhacks deleted the cte-union-nullability branch October 22, 2026 18:03
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

0