Lint rules
banshee ships 67 lint rules. Each has a dedicated page with the rationale, a before/after example, and how to configure or suppress it. Run banshee explain <CODE> for the same text on the command line.
Aliasing
Section titled “Aliasing”| Code | Fixable | Description |
|---|---|---|
| AL01 | yes | Table alias should be introduced with AS |
| AL02 | yes | Column alias should be introduced with AS |
| AL03 | — | Complex select expression should be aliased |
| AL04 | — | Duplicate table alias in one FROM |
| AL05 | — | Table alias declared but never used |
| AL08 | — | Duplicate column alias in a SELECT list |
Ambiguity
Section titled “Ambiguity”| Code | Fixable | Description |
|---|---|---|
| AM01 | — | DISTINCT is redundant with GROUP BY |
| AM02 | — | Set operators (UNION/EXCEPT/INTERSECT) should state ALL or DISTINCT |
| AM03 | — | ORDER BY mixes explicit and implicit sort directions |
| AM04 | yes | Avoid SELECT *; list columns explicitly (fix needs schema) |
| AM05 | — | Implicit cross join; use an explicit JOIN clause |
| AM07 | — | Set-operation branches select different column counts |
| AM09 | — | LIMIT/OFFSET without ORDER BY is non-deterministic |
Capitalisation
Section titled “Capitalisation”| Code | Fixable | Description |
|---|---|---|
| CP01 | yes | Keywords should be upper case |
| CP02 | yes | Unquoted identifiers should be lower case |
Convention
Section titled “Convention”| Code | Fixable | Description |
|---|---|---|
| CV01 | yes | Use <> instead of != for inequality |
| CV04 | yes | Use count(*) instead of count(1)/count(0) |
| CV05 | yes | Compare with NULL using IS NULL / IS NOT NULL |
| CV06 | yes | Statements should end with a semicolon |
| CV08 | — | Prefer LEFT JOIN over RIGHT JOIN |
| CV09 | — | Use of a configured blocked word |
| CV10 | yes | LIKE without a wildcard is just = |
| CV11 | — | Inconsistent cast style within a statement |
| CV13 | yes | IN with a single value is just = |
| Code | Fixable | Description |
|---|---|---|
| JB01 | yes | Use ->> when comparing a JSONB value to text |
Migration safety
Section titled “Migration safety”| Code | Fixable | Description |
|---|---|---|
| MG01 | yes | CREATE INDEX without CONCURRENTLY locks the table |
| MG02 | yes | ADD CONSTRAINT (FK/CHECK) without NOT VALID validates under a lock |
| MG03 | — | ADD COLUMN with a volatile DEFAULT rewrites the whole table |
| MG04 | — | ADD COLUMN NOT NULL without a DEFAULT fails on a non-empty table |
| MG05 | — | DROP COLUMN destroys data and breaks dependents |
| MG06 | — | ALTER COLUMN TYPE rewrites the table under a lock |
| MG07 | — | RENAME breaks code that refers to the old name |
| MG08 | — | TRUNCATE … CASCADE empties dependent tables too |
| MG09 | yes | Prefer text to char(n)/varchar(n) |
| MG10 | yes | Prefer timestamptz to timestamp |
| MG11 | — | Prefer bigint over a narrower integer for a primary key |
| MG12 | yes | DROP INDEX without CONCURRENTLY locks the table |
| MG13 | — | ADD PRIMARY KEY/UNIQUE builds its index under a lock |
| MG14 | — | ALTER COLUMN SET NOT NULL scans the table under a lock |
| MG15 | — | Prefer GENERATED … AS IDENTITY over serial |
| MG16 | — | DROP TABLE destroys the table and its dependents |
| MG17 | — | ALTER COLUMN DROP NOT NULL lets nulls into the column |
| MG18 | — | DROP DATABASE destroys the whole database |
| MG19 | — | CREATE INDEX CONCURRENTLY cannot run inside a transaction |
| MG20 | — | Transaction opened but never committed or rolled back |
| MG21 | — | BEGIN/START issued inside an open transaction |
| MG22 | — | CREATE/DROP without IF [NOT] EXISTS is not idempotent |
| MG23 | — | CREATE TABLE name is not schema-qualified |
| MG24 | — | Identifier exceeds Postgres’s 63-byte limit |
| MG25 | — | REINDEX without CONCURRENTLY locks the index for the rebuild |
| MG26 | — | VACUUM FULL / CLUSTER rewrite the table under a lock |
| MG27 | — | Lock-taking migration without statement/lock timeout |
| MG28 | — | CREATE DOMAIN with a constraint is validated under a lock |
| MG29 | — | ALTER DOMAIN ADD CONSTRAINT validates under a lock |
| MG30 | — | DETACH PARTITION without CONCURRENTLY holds an exclusive lock |
References
Section titled “References”| Code | Fixable | Description |
|---|---|---|
| RF01 | — | Reference to an unknown table, column or alias (needs schema) |
| RF02 | — | Ambiguous column; qualify with a table name (needs schema) |
| RF03 | — | Inconsistent column qualification in a single-table query |
| RF06 | yes | Identifier quoted unnecessarily |
Safety
Section titled “Safety”| Code | Fixable | Description |
|---|---|---|
| SF01 | — | UPDATE without WHERE affects all rows |
| SF02 | — | DELETE without WHERE affects all rows |
| SF03 | — | INSERT without an explicit column list |
Structure
Section titled “Structure”| Code | Fixable | Description |
|---|---|---|
| ST01 | yes | Redundant ELSE NULL in CASE |
| ST03 | — | CTE is defined but never used |
| ST05 | — | Subquery in FROM/JOIN; prefer a CTE |
| ST07 | — | Avoid NATURAL JOIN |
| ST08 | — | DISTINCT ON without ORDER BY is non-deterministic |