fix(cte): accept the MATERIALIZED hint on a CTE - #314
Merged
Conversation
`AS MATERIALIZED (...)` and `AS NOT MATERIALIZED (...)` are Postgres 12+
planner hints that sit between `as` and the body. ParseCteEntry wanted the
opening paren directly after `as`, so either spelling failed the match and
took the whole WITH clause with it. Skipping the hint is enough: it changes
how the CTE runs, not what it returns.
The failure was quiet rather than loud, and that is the second half of this.
ParseWithClause had no [never] guard, so `never extends { ctes: infer C
extends CteEntry[]; rest: infer R extends string }` passed with both infers
falling back to their constraints. ResolveCteContext then got a `rest` of
`string` and the query came back as `{ [x: string]: unknown }[]`, with
strict mode reporting `unknown table: ''`. Same [never] pattern as #229.
Under VERSIONING.md this is a minor: a query that failed to parse now
parses and types correctly.
Fixes #283
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tiagolauer
force-pushed
the
fix/283-cte-materialized
branch
from
August 2, 2026 13:05
be669f8 to
d744a72
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #283.
The bug
AS MATERIALIZED (...)andAS NOT MATERIALIZED (...)are Postgres 12+ planner hints that sit betweenasand the body.ParseCteEntrywanted the opening paren directly afteras, so either spelling failed the match and took the whole WITH clause with it.The failure was quiet rather than loud, which is the second half of this.
ParseWithClausehad no[never]guard, sonever extends { ctes: infer C extends CteEntry[]; rest: infer R extends string }passed with both infers falling back to their constraints.ResolveCteContextthen got arestofstring, and the query degraded into an index signature row instead of reporting anything.The fix
Skip the hint, and add the guard. Skipping is enough on its own because the hint changes how the CTE is executed, not what it returns. The guard is the same
[never]patternExtraSourcesAfterKeywordcarries from #229, and it means the next unparseable WITH clause fails cleanly instead of degrading.Versioning
Minor: a query that failed to parse now parses and types correctly.
Verification
tests/cte-materialized.test-d.ts, seven cases red on master:materializedandnot materializedwith recursiveand the hint togetherTwo controls stay pinned: a plain CTE is unchanged, and a column actually named
materializedis still read as a column rather than the hint.tsc --noEmitclean, 192 unit tests pass, budget 192,881 (+395 over master).Found during an audit pass; fix and tests written with Claude Code.