Skip to content

feat(columns): expose domain identity on domain-typed columns#1093

Open
coderdan wants to merge 1 commit into
supabase:masterfrom
coderdan:fix/columns-domain-identity
Open

feat(columns): expose domain identity on domain-typed columns#1093
coderdan wants to merge 1 commit into
supabase:masterfrom
coderdan:fix/columns-domain-identity

Conversation

@coderdan

@coderdan coderdan commented Jul 24, 2026

Copy link
Copy Markdown

Fixes #1092.

Authored using Claude/Opus 4.8.
All code manually reviewed be me before PR was opened :)

Problem

For a column whose type is a DOMAIN, the columns query resolves the column to the domain's base type and never surfaces the domain itself. Both output fields that could carry the type name collapse to the base type:

  • format uses COALESCE(bt.typname, t.typname), and bt is joined only for domains so for a domain the base type always wins over the domain's own t.typname.
  • data_type uses format_type(t.typbasetype, NULL) for a domain whose base type lives in pg_catalog.

So a jsonb column and a column of a domain over jsonb are indistinguishable in the API response. The Studio table editor grid and schema visualizer therefore label domain columns with the base type, while the "New column" type picker shows the domain name - it reads from the types query, which uses format_type(t.oid) and is domain-aware. The same type renders inconsistently across the UI. psql \d gets this right for the same reason.

Change

Adds domain_schema and domain_name to the columns output. Both are null unless the column's type is a domain:

{
  "name": "search",
  "data_type": "jsonb",
  "format": "jsonb",
  "domain_schema": "public",
  "domain_name": "eql_v3_text_search"
}

This follows information_schema.columns — which columns.sql.ts says in its first line it is adapted from — where data_type reports the underlying type and the domain identity is carried in separate domain_* columns.

COLUMNS_SQL is the single source of column introspection for tables, views, materialized views and foreign tables, so all five endpoints pick this up.

Why additive, rather than changing format

Making format report the domain name looks like the more direct fix, but I think it would break type generation.

Keeping format as the base type means generators are entirely unaffected. The regenerated typegen snapshots confirm it — for the new fixtures, a domain over jsonb still resolves to Json | null, and a domain over int4 NOT NULL to a non-nullable number.

Scope

Arrays of domains are not covered. information_schema does the same — for an array column it reports data_type = 'ARRAY' with domain_name null, and the domain identity belongs to the element type. Happy to extend this through t.typelem if you'd prefer it here.

Tests

New fixtures in test/db/00-init.sql (a domain over jsonb, and a NOT NULL domain over int4) plus a test in test/lib/columns.ts whose snapshot is the before/after of the bug - search and plain are both jsonb in format but are now distinguishable:

{ "name": "search", "format": "jsonb", "domain_name": "text_search", "domain_schema": "public" }
{ "name": "plain",  "format": "jsonb", "domain_name": null,          "domain_schema": null }

The remaining snapshot churn is the mechanical domain_name: null, domain_schema: null addition across existing column assertions.

For a column whose type is a DOMAIN, the columns query resolved the
column to the domain's base type and never surfaced the domain itself.
Both fields that could carry the type name collapsed to the base type:

  - `format` used COALESCE(bt.typname, t.typname), and `bt` is joined
    only for domains, so the base type always won.
  - `data_type` used format_type(t.typbasetype, NULL) for a domain whose
    base type lives in pg_catalog.

Downstream consumers had no way to tell a `jsonb` column apart from a
column of a domain over `jsonb`, so the Studio table editor grid and
schema visualizer label such columns with the base type, while the
"New column" type picker shows the domain name because it reads from
the types query (format_type(t.oid)). The same type rendered
inconsistently across the UI.

Add `domain_schema` and `domain_name`, both null unless the column's
type is a domain. This follows information_schema.columns, which
columns.sql.ts is adapted from: `data_type` there reports the underlying
type and the domain is carried in separate domain_* columns.

The addition is deliberately non-breaking. `format` still reports the
base type because all four type generators feed it into resolvers that
only know base types, enums, composites, tables and views; a domain name
would fall through to `unknown` and silently regress generated types for
every domain column. The typegen snapshots confirm domain columns still
resolve to their base type (Json, number).

COLUMNS_SQL is the single source for tables, views, materialized views
and foreign tables, so all five endpoints pick this up.

As with information_schema, arrays of domains are not covered: the
domain identity there belongs to the element type.

Fixes supabase#1092
@coderdan
coderdan marked this pull request as ready for review July 24, 2026 11:05
@coderdan
coderdan requested review from a team, avallete and soedirgo as code owners July 24, 2026 11:05
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.

columns introspection reports a domain column's **base type**, discarding the domain identity

1 participant