ReferenceLint
Lint
Lint rules point at schema design that compiles but is likely a mistake: a reference column with no index, a table with no primary key, a table nothing joins, a column name typed two ways, an index another one covers. A finding is a warning that carries its rule's name, and it never stops the drawing.
Running it
The playground runs the rules on every change: the status bar counts the findings, pressing the count lists them, and each has a dotted underline in the editor (see Playground). On the command line, --lint checks a file and prints only the problems, so it fits a CI step:
npx resin schema.erd --lint
schema.erd:8:3: warning: reference column `user_id` has no index [ref-index] | 8 | user_id bigint -> users | ^^^^^^^ = hint: add `index` to the column, or start a composite index with it schema.erd:9:21: warning: the index on (`status`) is covered by `ix_status_id` (`status`, `id`) [dup-index] | 9 | status varchar index | ^^^^^ = hint: remove it; the wider index serves lookups on (`status`) too
It exits with status 1 when there is an error or a finding, and 0 when the file is clean. In code, run lint(result.doc) on a document that compiled; see Library.
The rules run only on a document without errors: they need its references resolved.
ref-index
A column that holds a reference (-> or ~>) needs an index that starts with it. Joining a parent to its children, or deleting a parent, looks the children up by that column; without an index the database reads the whole table. PostgreSQL does not create these indexes for foreign keys by itself.
table customers { id bigint pk } table orders { id bigint pk customer_id bigint -> customers }
reference column `customer_id` has no index. Add index to the column, or start a composite index with it. The column's uk, an index(...) or unique(...) that lists it first, and the primary key, when the column is its first column, all count.
no-pk
A table of your own needs a primary key: without one, rows cannot be told apart, and tools that replicate or audit rows refuse the table. External tables are left out.
table `audit_log` has no primary key. Mark its key column pk.
type-drift
Columns of one name usually hold one kind of value, so they should have one type. When user_id is bigint in most tables and int in one, the odd one is reported:
table orders { id bigint pk user_id bigint } table carts { id bigint pk user_id bigint } table reviews { id bigint pk user_id int }
column `user_id` is int here but bigint in `orders`, `carts` . The type most of the columns have is taken as the norm. Type arguments are not compared, so varchar(20) and varchar(40) agree. A column whose reference already has a type mismatch warning is left out, so the same problem is not reported twice.
dup-index
An index whose columns are the start of another index's columns serves no lookup the wider one does not, and every index slows down writes:
table orders { id bigint pk status varchar index created_at datetime index(status, created_at) as ix_status_time }
the index on (`status`) is covered by `ix_status_time` (`status`, `created_at`). Remove the narrower one. Two indexes over the same columns are reported once, at the later one, and a uk or unique(...) over exactly the primary key's columns is reported as repeating it.
The rules at a glance
| Rule | Finds |
|---|---|
ref-index |
A reference column that no index starts with |
no-pk |
A table without a primary key |
unrelated |
A table no reference joins to another table |
type-drift |
A column whose type differs from the other columns of its name |
dup-index |
An index another index covers, or a unique constraint that repeats the primary key |
Composite references
A composite reference needs an index, unique key or primary key whose leading columns match the ordered source list. The ref-index warning points at the whole constraint. A composite relation also counts when checking whether a table is unrelated.