Skip to content

feat: type SQL macro calls everywhere, with optional returns, TYPE OF, ANY families and __cte - #1247

Merged
kvlonge merged 29 commits into
mainfrom
feat/sql-macro-typing
Oct 24, 2026
Merged

kvlonge merged 29 commits into
mainfrom
feat/sql-macro-typing

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Why

A SQL macro's returns type was applied by a post-pass that only saw whole select items in a few
positions, returns was mandatory but never checked against the body, and a CTE-list macro could
be called only once per model.

Changes

  • Analysis wraps each expression macro call in an analysis-only Polyglot marker, so returns
    applies wherever the expanded SQL's type is unknown, or partly unknown of the same shape. A known
    type stands. Compiled SQL is unchanged. Post-pass removed. A marker never changes how a call's
    position parses in the adapter's dialect; bare-word calls stay unmarked.
  • returns is optional and validated per adapter dialect, with decimal ranges (P015). It is
    checked against a body whose type is known, not guessed, at the macro file (P024).
  • returns TYPE OF <argument>; ANY NUMERIC, ANY TEXT, ANY TEMPORAL arguments, checked at the
    call (P017) and in the body (P025).
  • __cte := name names the CTEs a CTE-list call inserts, renaming references by scope,
    including parenthesised joins.
  • K003 and SQBRCONTRACT105 suggest returns; SQBRCONTRACT105 judges a whole typed call by it.
  • A marked set-operation model is re-typed once its inputs are known; no golden changes.
  • Cache format bumped; __polyglot_typed reserved.
  • Polyglot moves to the typed markers on upstream 0.13.4; ORDER BY ALL and decimals wider than a
    dialect holds keep their behaviour.
  • Docs updated.

Verification

  • E2E: typing matrix, keyword slots, grouping keys; returns forms, shapes, adapters; TYPE OF;
    families; __cte scoping and builds; warm cache; --since.
  • make test, make check-ci, make compiler-differential. Changed goldens:
    sql-macro-invalid-definition (missing returns is valid), sql-macro-cte-name-clash (help
    suggests __cte); new: sql-macro-returns-disagrees-with-body,
    sql-macro-body-misuses-an-any-family. Generated-type golden: invalid nested DECIMAL(0) is now
    spelled generically.

…name

Polyglot lowers __polyglot_typed(expr, 'TYPE') into a typed expression node.
Authored SQL may not use that name, and the analysis cache format moves to
v3 so entries typed by the old post-pass are dropped.
…ed query

Expansion returns the byte range of every expression macro call, top-level
and nested, through body substitution, declaration rendering, Python macro
splicing and cursor canonicalisation. Calls inside Python macro arguments
are not recorded.
The analysed text wraps each call to a macro with returns in the typed
marker; the compiled SQL is unchanged. Binding diagnostics map back through
the marker insertions.
Replace the declared-output post-pass with the typed marker: analysis
types every recorded call from its SQL and falls back to the declared
return type only when inference is unknown. Typed calls flow through
compiled facts and rule rows, and the explicit-cast rule accepts whole
macro calls with a return type by position.
An expression macro may omit returns; its calls are then typed by
inference alone. When returns is declared, the body is typed once from
stand-ins for its declared arguments, and a known type of another family
is reported as P024 at the macro file. K003 and the explicit-cast rule
suggest declaring returns when an output is one call to a macro without
it.
A CTE-list macro call may pass __cte := name. The macro's last CTE then
takes that name and its other CTEs become <name>__<cte>, renamed with
their references by parsing; arguments are never renamed. Calls without
__cte behave as before, and a repeated call now suggests __cte. The
formatter also accepts comma-less CTE-list calls in a row.
returns TYPE OF <argument> types each call as its argument through the
typed-like analysis marker, where the body's own type is unknown. An ANY
argument may be narrowed to ANY NUMERIC, ANY TEXT or ANY TEMPORAL: calls
are checked against the family at the argument, and the body is checked
at the macro file with the family's widest member (P025). Bumps polyglot
to the typed-like marker.
A marker asserted its type while an upstream star model's columns were
still untyped, so a known column inside a call took the declared type.
Such models are now enriched once their inputs are known, and the
enrichment's types replace the earlier assertions.
@kvlonge
kvlonge enabled auto-merge (squash) October 24, 2026 14:23
@kvlonge
kvlonge merged commit 4e07586 into main Oct 24, 2026
86 of 88 checks passed
@kvlonge
kvlonge deleted the feat/sql-macro-typing branch October 24, 2026 15:49
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