Skip to content

feat(migrate): expression/functional indexes (identity.secondary @expr + @using)#45

Merged
dmealing merged 2 commits into
mainfrom
feat/expression-indexes
Jun 20, 2026
Merged

feat(migrate): expression/functional indexes (identity.secondary @expr + @using)#45
dmealing merged 2 commits into
mainfrom
feat/expression-indexes

Conversation

@dmealing

Copy link
Copy Markdown
Member

What

Adds functional/expression index support to the schema toolchain. Previously, expression-key indexes (e.g. CREATE INDEX … ON t (lower(email)), or a GIN index over string_to_array(...)) could not be modeled and were silently skipped by introspect — so a reverse-engineered/regenerated schema lost them.

Model

identity.secondary gains two RDB-physical db-provider attrs:

  • @expr — the raw key expression (e.g. lower((email)::text)). Used instead of the column list. @fields still anchors the underlying column(s) (satisfies the required-@fields rule); the expression is the actual key.
  • @using — access method (gin/gist/hash/…); default btree, not rendered.

Engine

  • IndexDescriptor: expr?, using?
  • buildExpectedSchema: reads @expr/@using (expr ⇒ columns: [])
  • emit (postgres): CREATE [UNIQUE] INDEX … ON … [USING <method>] (<expr-or-cols>) [WHERE …]
  • introspect (postgres): captures pg_am.amname + lifts the key expression from pg_get_indexdef — expression-key indexes are no longer skipped
  • diff/indexEquals: compares using + normalized expr

Cross-port + fixtures

Registered the two attrs in all ports (TypeScript, Python, Java/Kotlin, C#); updated spec/metamodel/db.json + the embedded def; regenerated the registry-conformance manifest, the coverage snapshot, and the metamodel docs.

Tests

expression-index.test.ts — round-trips a btree functional index (lower(email)) and a GIN expression index through buildExpectedSchema + emit. Full migrate-ts + metadata suites green (2534 tests).

🤖 Generated with Claude Code

dmealing and others added 2 commits June 19, 2026 11:38
…+ @using)

Adds support for functional/expression indexes — e.g. `lower(email)` (btree) and
`string_to_array(tags, ',')` (GIN) — which previously could not be modeled and were
silently skipped on introspect.

- identity.secondary gains two db-provider attrs:
  - @expr  — raw key EXPRESSION; used instead of @fields (which then anchors the
             underlying column(s) for the loader's required @fields)
  - @using — access method (gin/gist/hash/…); default btree (not rendered)
- IndexDescriptor: expr?, using?
- buildExpectedSchema: reads @expr/@using
- emit (postgres): renders `USING <method> (<expr>)`
- introspect (postgres): captures the access method + lifts the key expression from
  pg_get_indexdef (expression-key indexes are no longer skipped)
- diff/indexEquals: compares using + normalized expr
- registered across all ports (TS/Python/Java+Kotlin/C#); spec + embedded def updated;
  registry-conformance manifest, coverage snapshot, and metamodel docs regenerated
- test: expression-index round-trip (expected-schema + emit) for btree functional +
  GIN expression indexes

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…using additions

The repo-root spec/metamodel/db.json is mirrored byte-for-byte into each port's
embedded spec copy (FR-033 single-source). Re-copied after adding @expr/@using.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dmealing
dmealing merged commit df789bc into main Jun 20, 2026
29 checks passed
@dmealing
dmealing deleted the feat/expression-indexes branch June 20, 2026 06:00
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