Skip to content

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.

CodeFixableDescription
AL01yesTable alias should be introduced with AS
AL02yesColumn alias should be introduced with AS
AL03Complex select expression should be aliased
AL04Duplicate table alias in one FROM
AL05Table alias declared but never used
AL08Duplicate column alias in a SELECT list
CodeFixableDescription
AM01DISTINCT is redundant with GROUP BY
AM02Set operators (UNION/EXCEPT/INTERSECT) should state ALL or DISTINCT
AM03ORDER BY mixes explicit and implicit sort directions
AM04yesAvoid SELECT *; list columns explicitly (fix needs schema)
AM05Implicit cross join; use an explicit JOIN clause
AM07Set-operation branches select different column counts
AM09LIMIT/OFFSET without ORDER BY is non-deterministic
CodeFixableDescription
CP01yesKeywords should be upper case
CP02yesUnquoted identifiers should be lower case
CodeFixableDescription
CV01yesUse <> instead of != for inequality
CV04yesUse count(*) instead of count(1)/count(0)
CV05yesCompare with NULL using IS NULL / IS NOT NULL
CV06yesStatements should end with a semicolon
CV08Prefer LEFT JOIN over RIGHT JOIN
CV09Use of a configured blocked word
CV10yesLIKE without a wildcard is just =
CV11Inconsistent cast style within a statement
CV13yesIN with a single value is just =
CodeFixableDescription
JB01yesUse ->> when comparing a JSONB value to text
CodeFixableDescription
MG01yesCREATE INDEX without CONCURRENTLY locks the table
MG02yesADD CONSTRAINT (FK/CHECK) without NOT VALID validates under a lock
MG03ADD COLUMN with a volatile DEFAULT rewrites the whole table
MG04ADD COLUMN NOT NULL without a DEFAULT fails on a non-empty table
MG05DROP COLUMN destroys data and breaks dependents
MG06ALTER COLUMN TYPE rewrites the table under a lock
MG07RENAME breaks code that refers to the old name
MG08TRUNCATE … CASCADE empties dependent tables too
MG09yesPrefer text to char(n)/varchar(n)
MG10yesPrefer timestamptz to timestamp
MG11Prefer bigint over a narrower integer for a primary key
MG12yesDROP INDEX without CONCURRENTLY locks the table
MG13ADD PRIMARY KEY/UNIQUE builds its index under a lock
MG14ALTER COLUMN SET NOT NULL scans the table under a lock
MG15Prefer GENERATED … AS IDENTITY over serial
MG16DROP TABLE destroys the table and its dependents
MG17ALTER COLUMN DROP NOT NULL lets nulls into the column
MG18DROP DATABASE destroys the whole database
MG19CREATE INDEX CONCURRENTLY cannot run inside a transaction
MG20Transaction opened but never committed or rolled back
MG21BEGIN/START issued inside an open transaction
MG22CREATE/DROP without IF [NOT] EXISTS is not idempotent
MG23CREATE TABLE name is not schema-qualified
MG24Identifier exceeds Postgres’s 63-byte limit
MG25REINDEX without CONCURRENTLY locks the index for the rebuild
MG26VACUUM FULL / CLUSTER rewrite the table under a lock
MG27Lock-taking migration without statement/lock timeout
MG28CREATE DOMAIN with a constraint is validated under a lock
MG29ALTER DOMAIN ADD CONSTRAINT validates under a lock
MG30DETACH PARTITION without CONCURRENTLY holds an exclusive lock
CodeFixableDescription
RF01Reference to an unknown table, column or alias (needs schema)
RF02Ambiguous column; qualify with a table name (needs schema)
RF03Inconsistent column qualification in a single-table query
RF06yesIdentifier quoted unnecessarily
CodeFixableDescription
SF01UPDATE without WHERE affects all rows
SF02DELETE without WHERE affects all rows
SF03INSERT without an explicit column list
CodeFixableDescription
ST01yesRedundant ELSE NULL in CASE
ST03CTE is defined but never used
ST05Subquery in FROM/JOIN; prefer a CTE
ST07Avoid NATURAL JOIN
ST08DISTINCT ON without ORDER BY is non-deterministic