Skip to content

DatabaseTableSchema

DatabaseTableSchema shows one table’s shape - columns, keys, constraints, defaults, the indexes behind its access paths, and engine-specific DDL outside the schema grammar - as a dense psql-style grid with titled verbatim-SQL bands, so a plan that proposes or changes a table can be reviewed on the schema itself instead of prose approximations of it.

The figure opens with a header band carrying the schema-qualified name and table note, above a Column | Type | Constraints | Default | Comment grid in which every column is exactly one equal-rhythm row. The Constraints cell carries key badges, explicit nullability, foreign keys with their actions, check expressions, and numbered index references beside the column they govern.

The tinted Indexes band below the grid numbers each index (INDX 1, INDX 2, …) and leads with its name; the columns, method, and partial predicate follow in demoted muted text with the invariant note beneath. A column that an index uses as a key carries the matching INDX n pill, while a column that only appears in a partial-index predicate is marked WHERE INDX n, so a reviewer can answer “is this column indexed?” without leaving the row.

Each Ddl child adds a titled band of verbatim SQL after the indexes - row security, triggers, partitioning, grants, whatever the schema grammar deliberately leaves out. Every band renders stacked and labeled, so nothing is hidden from the reader.

The rendering is optimized for Postgres but generic: column types are opaque text, any engine’s types render unchanged, and the component never parses Ddl SQL - it displays exactly what was authored.

Use DatabaseTableSchema whenever a plan proposes or modifies a table and the reviewer must judge its shape: keys, nullability, defaults, relationships, and the indexes that back the access paths.

  • Exact DDL the reader will execute standalone - use a fenced sql block, which preserves every clause verbatim outside the figure
  • Multi-table relationship overviews - render one component per table; a diagram of the whole schema is a different concern
  • Restating the columns grid as a CREATE TABLE inside a Ddl child - that creates a second source of truth the component cannot check against the grid; reserve Ddl for what the grammar deliberately leaves out
  • Propose a new table and let the reviewer approve its keys, constraints, and indexes before a migration exists
  • Show the current shape of a table a plan is about to alter, beside a second component showing the planned shape
<DatabaseTableSchema name="catalog.refresh_jobs">
```dbml
id bigint [pk, increment]
cache_key text [not null, note: 'The catalog cache key this job refreshes.']
requested_by bigint [ref: > catalog.api_instances.id, delete: set null]
attempts integer [not null, default: 0, check: 'attempts <= 5']
status text [not null, default: 'queued', note: 'Allowed: queued | running | done | failed.']
enqueued_at timestamptz [not null, default: `now()`]
indexes {
cache_key [unique, name: 'refresh_jobs_live_key_idx', where: 'status <> \'done\'', note: 'Ensures one unfinished job per cache key.']
(status, enqueued_at) [name: 'refresh_jobs_scan_idx']
}
Note: 'One row per queued catalog refresh.'
```
<Ddl title="Row security">
```sql
ALTER TABLE catalog.refresh_jobs ENABLE ROW LEVEL SECURITY;
```
</Ddl>
</DatabaseTableSchema>
Attribute Type Required Behavior
name string (non-empty) Yes The table identity, optionally schema-qualified (catalog.refresh_jobs); the last dot splits the muted schema prefix from the emphasized table name.

Any other attribute is a positional authoring error.

The component takes exactly one fenced code block with the dbml language, followed by any number of Ddl children.

Each Ddl child carries one titled band of verbatim SQL: a required non-empty title attribute, unique within the component after surrounding and repeated whitespace are normalized, and exactly one fenced code block with the sql language as its whole body. The component displays the statements exactly as authored and highlighted, never parsed or validated as SQL, so engine-specific DDL renders truthfully for any engine.

The fence carries a deliberate subset of DBML - column lines, one optional indexes { ... } block, and one optional table Note: - with Postgres-motivated referential-action and partial-index extensions. Every violation reports a positional diagnostic pointing at the offending fence line, and out-of-subset DBML (Table blocks, standalone Ref: lines, Enum blocks) is rejected with the supported alternative named in the message.

Each column is one line, name type [settings]:

  • The name must be a unique bare identifier, and the fence must declare at least one column; the type is opaque text and may contain spaces (timestamp with time zone) or array brackets (text[]).
  • Marker settings are bare: pk (or primary key), not null, null, unique, increment (rendered as an Identity badge). Either primary-key spelling implies not null; combining null with pk or not null is rejected.
  • default: takes a number, true, false, null, a quoted string, or a backtick expression such as `now()`.
  • note: and check: take a single-line quoted value, and a check cannot be empty; notes render in the column’s Comment cell, and checks render as CHECK (...) in its Constraints cell.
  • ref: > table.column or ref: > schema.table.column declares a many-to-one foreign key; other relationship operators are outside the subset. delete: and update: add referential actions (cascade, restrict, set null, set default, no action) and require the ref: on the same line.

Each indexes block entry is a declared column, a composite (a, b) tuple whose members are declared columns or backtick expressions, or a backtick expression on its own. Every backtick expression must span its whole entry or tuple member. Its optional settings are the bare unique marker; a quoted, non-empty name: that must be unique in the table; type: (btree, hash, gin, gist); a quoted note:; and the extension where: with a non-empty quoted partial-index predicate. When an expression names a declared column directly, including as a double-quoted identifier, the column receives the matching INDX n pill. Only direct unqualified references drive those pills; SQL string literals are ignored.

The table Note: is the table’s comment, headed for COMMENT ON TABLE: one sentence saying what a single row represents. Operational behavior - pruning schedules, rollout plans, lifecycle narration - belongs in the prose around the component, not inside the schema.

Triggers, row-level security, partitioning, storage parameters, collation, and grants are deliberately not part of the schema grammar; they enter as verbatim SQL through Ddl children instead. Decision-worthy rationale still belongs in a Callout beside the component, and one-line facts in the table Note:.

The grid and every section are server-rendered in authored order, and a wide grid scrolls inside the figure instead of widening the page. With the viewer script active, drag a column header to reorder it or focus the header and use Left / Right; keyboard moves are announced. The columns menu can hide non-identity columns, persists order and visibility for this table in this exact plan revision, and resets both choices with Reset column layout. With scripts disabled, the complete authored grid remains visible in authored order and no dormant controls appear. The INDX n and WHERE INDX n references remain compact visual links between columns and the numbered index list. With the viewer script active, each reference is a focusable button that moves focus to its named index and briefly tints it; keyboard activation is native, and reduced-motion preferences remove the scroll and tint transitions.