Skip to content

core: carry enums, views and missing placeholders through the analysis core - #4614

Open
kyleconroy wants to merge 3 commits into
mainfrom
claude/magical-lamport-ckjxj8
Open

core: carry enums, views and missing placeholders through the analysis core#4614
kyleconroy wants to merge 3 commits into
mainfrom
claude/magical-lamport-ckjxj8

Conversation

@kyleconroy

@kyleconroy kyleconroy commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Three fixes from a triage of every internal/endtoend replay case that passes as committed but fails under SQLCEXPERIMENT=coreanalyzer. Together they take the core context from 347 failing cases to 281, with no case regressing and the base context still fully green. Each commit stands on its own.

Enums absent from models (50 cases, 45 fixed)

The core catalog created an enum type on CREATE TYPE ... AS ENUM but kept none of its labels, and the dump that hands the catalog to codegen carried relations only. Every enum was missing from models.go, PostgreSQL enum columns were typed any and MySQL ENUM columns string.

  • sql_enum_label stores an enum's labels, modeled on pg_enum.
  • The schema package applies ALTER TYPE ADD VALUE (with BEFORE/AFTER and IF NOT EXISTS), RENAME VALUE, RENAME TO, SET SCHEMA and DROP TYPE.
  • A MySQL ENUM or SET column declares an enum of its own, named <table>_<column> as the legacy catalog names it. It follows the column and the table through renames, is replaced on MODIFY, and goes with a dropped column or table.
  • Type names keep the schema an engine reports on the TypeName itself and drop a spelled-out public., so foo.mood and DROP TYPE public.status resolve the way their column references do.

The five leftovers are blocked by other buckets (varchar typed any, MySQL BOOLEAN, sqlc.embed).

Views absent from models (10 cases, 8 fixed)

The catalog dump listed kind r relations only, so views, materialized views and CREATE TABLE AS had no model, and a query selecting all of one got a row struct of its own. The listing now covers the relations a query selects rows from the same way. The two leftovers are the anonymous-column naming bucket (SELECT 1 as a view) and CREATE SCHEMA.

Placeholders the analyzer walked past (17 cases, 14 fixed)

  • INSERT ... SELECT: the engines report the source query with an empty VALUES list rather than none, so the analyzer took the VALUES branch and walked nothing. A placeholder selected directly now takes the type of the column it lands in.
  • LIMIT on UPDATE, DELETE, and on UNION/INTERSECT/EXCEPT was never looked at.
  • ON DUPLICATE KEY UPDATE assignments bind the way SET does.
  • x IN (SELECT ...) on MySQL arrives wrapped in a sublink, which the IN node now looks through.
  • x COLLATE c: SQLite puts the expression in the node's other field, so the placeholder underneath was neither found nor typed.
  • CALL: the compiler now sends it through the core, CREATE PROCEDURE is recorded with a void pseudo type as its result, and a call types each placeholder from the declared parameter, by position or by name =>. The AST gains IsProcedure, set by both parsers, since a function with only OUT parameters also declares no return type.
  • A placeholder on the left of an IN list takes its type from the members.

The three leftovers are MySQL BOOLEAN and LIMIT parameter naming. I tried naming LIMIT/OFFSET placeholders in the core and backed it out: it changed analyze output for SQLite and ClickHouse, where the database reports no name, so that convention belongs in the compiler bridge rather than the analysis.

Notes for review

  • One judgement call: a MySQL ENUM column is now reported by sqlc analyze with the synthetic <table>_<column> type name rather than enum. That is what codegen needs and what the legacy compiler always did, but it is not what MySQL itself reports. goldeneye has no MySQL analyze check today.
  • No test cases were added; the existing corpus covers the changes. Verified by running TestReplay/base (all green) and SQLC_TEST_CORE=1 TestReplay/core (347 → 281) before and after each commit.
  • internal/core/catalogdb is regenerated from the catalog schema and query changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XZcKb4GFiZQbeo9oVmyF3m

The core catalog created an enum type on CREATE TYPE ... AS ENUM but kept
none of its labels, and the dump that hands the catalog to codegen carried
relations only. Under SQLCEXPERIMENT=coreanalyzer every enum was missing
from models.go, PostgreSQL enum columns were typed any and MySQL ENUM
columns string.

The catalog now stores enum labels (sql_enum_label, modeled on pg_enum)
and the schema package applies the statements that change them: ALTER TYPE
ADD VALUE (with BEFORE/AFTER and IF NOT EXISTS), RENAME VALUE, RENAME TO,
SET SCHEMA, and DROP TYPE. A MySQL ENUM or SET column declares an enum of
its own, named <table>_<column> as the legacy catalog names it; it follows
the column and the table through renames, is replaced on MODIFY, and goes
with a dropped column or table. The dump adds each enum to its schema so
codegen builds the same Go types either way a query set was analyzed.

Type names now keep the schema an engine reports on the TypeName itself
and drop a spelled-out "public.", so foo.mood and public.status resolve
the way their column references do.

In the core replay context 46 more cases pass, with no case regressing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZcKb4GFiZQbeo9oVmyF3m
The dump that carries the core catalog to codegen listed tables only, so
under SQLCEXPERIMENT=coreanalyzer a view, a materialized view or a table
created from a query had no model in models.go, and a query selecting all
of one got a row struct of its own instead of the view's model.

The listing now covers the relations a query selects rows from the same
way, which is what codegen builds a model for.

In the core replay context 8 more cases pass, with no case regressing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZcKb4GFiZQbeo9oVmyF3m
Under SQLCEXPERIMENT=coreanalyzer several statement shapes lost their
parameters, so the generated function took fewer arguments than the query
has placeholders:

- INSERT ... SELECT: the engines report the source query with an empty
  VALUES list rather than none, so the analyzer took the VALUES branch and
  walked nothing. The query is analyzed now, and a placeholder selected
  directly stands in for the column it lands in.
- UPDATE ... LIMIT and DELETE ... LIMIT, and LIMIT/OFFSET on a UNION,
  INTERSECT or EXCEPT, were never looked at.
- ON DUPLICATE KEY UPDATE assignments were never looked at; they bind the
  way SET does.
- x IN (SELECT ...) on MySQL arrives wrapped in a sublink, which the IN
  node did not look through.
- x COLLATE c: SQLite puts the expression in the node's other field and
  the collation's name where PostgreSQL puts the expression, so the
  placeholder underneath was neither found nor typed.
- CALL: the compiler only sent SELECT, INSERT, UPDATE and DELETE through
  the core, and the schema package skipped CREATE PROCEDURE. A procedure is
  recorded now, with a void pseudo type as its result, and a CALL types
  each placeholder from the procedure's declared parameter, by position or
  by name, and names it after the parameter. The AST gains IsProcedure,
  which both parsers set, since a function with only OUT parameters also
  declares no return type.

A placeholder on the left of an IN list takes its type from the members,
the way the other operand of a comparison would.

In the core replay context 12 more cases pass, with no case regressing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZcKb4GFiZQbeo9oVmyF3m
@kyleconroy
kyleconroy force-pushed the claude/magical-lamport-ckjxj8 branch from 6c75a73 to 8a6f99d Compare September 9, 2026 06:14
@kyleconroy kyleconroy changed the title core: carry enums, unsigned, views and missing placeholders through the analysis core core: carry enums, views and missing placeholders through the analysis core Sep 9, 2026
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