Rules
Below the list of rules supported by the Postgres Language Server, divided by group. Here's a legend of the emojis:
- The icon ✅ indicates that the rule is part of the recommended rules.
Safety
Rules that detect potential safety issues in your code.
| Rule name | Description | Properties |
|---|---|---|
| addSerialColumn | Adding a column with a SERIAL type or GENERATED ALWAYS AS ... STORED causes a full table rewrite. | ✅ |
| addingFieldWithDefault | Adding a column with a DEFAULT value may lead to a table rewrite while holding an ACCESS EXCLUSIVE lock. | ✅ |
| addingForeignKeyConstraint | Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes. | ✅ |
| addingNotNullField | Setting a column NOT NULL blocks reads while the table is scanned. | ✅ |
| addingPrimaryKeyConstraint | Adding a primary key constraint results in locks and table rewrites. | ✅ |
| addingRequiredField | Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required. | |
| avoidAddingExclusionConstraint | Adding an exclusion constraint acquires an ACCESS EXCLUSIVE lock. |
✅ |
| avoidAlterEnumAddValue | ALTER TYPE ... ADD VALUE cannot run inside a transaction block in older Postgres versions. |
|
| avoidAttachingPartition | Attaching a partition acquires an ACCESS EXCLUSIVE lock on the parent table. |
✅ |
| avoidCreateTrigger | Creating a trigger acquires a SHARE ROW EXCLUSIVE lock on the table. |
|
| avoidEnableDisableTrigger | Enabling or disabling a trigger acquires a SHARE ROW EXCLUSIVE lock. |
|
| avoidWideLockWindow | Acquiring ACCESS EXCLUSIVE locks on multiple tables widens the lock window. | ✅ |
| banCharField | Using CHAR(n) or CHARACTER(n) types is discouraged. | |
| banConcurrentIndexCreationInTransaction | Concurrent index creation is not allowed within a transaction. | ✅ |
| banDeleteWithoutWhere | A DELETE statement without a WHERE clause will remove all rows from the table. |
✅ |
| banDropColumn | Dropping a column may break existing clients. | ✅ |
| banDropDatabase | Dropping a database may break existing clients (and everything else, really). | |
| banDropNotNull | Dropping a NOT NULL constraint may break existing clients. | ✅ |
| banDropSchema | Dropping a schema will remove all objects within it and may break existing clients. | ✅ |
| banDropTable | Dropping a table may break existing clients. | ✅ |
| banDropTrigger | Dropping a trigger acquires an ACCESS EXCLUSIVE lock on the table. |
|
| banTruncate | Truncating a table removes all rows and can cause data loss in production. | ✅ |
| banTruncateCascade | Using TRUNCATE's CASCADE option will truncate any tables that are also foreign-keyed to the specified tables. |
|
| banUpdateWithoutWhere | An UPDATE statement without a WHERE clause will modify all rows in the table. |
✅ |
| banVacuumFull | VACUUM FULL rewrites the entire table and acquires an ACCESS EXCLUSIVE lock. |
✅ |
| changingColumnType | Changing a column type may require a table rewrite and break existing clients. | |
| concurrentRefreshMatviewLock | REFRESH MATERIALIZED VIEW CONCURRENTLY still acquires an EXCLUSIVE lock. |
|
| constraintMissingNotValid | Adding constraints without NOT VALID blocks all reads and writes. | |
| creatingEnum | Creating enum types is not recommended for new applications. | |
| disallowUniqueConstraint | Disallow adding a UNIQUE constraint without using an existing index. | |
| lockTimeoutWarning | Taking a dangerous lock without setting a lock timeout can cause indefinite blocking. | ✅ |
| multipleAlterTable | Multiple ALTER TABLE statements on the same table should be combined into a single statement. | ✅ |
| preferBigInt | Prefer BIGINT over smaller integer types. | |
| preferBigintOverInt | Prefer BIGINT over INT/INTEGER types. | |
| preferBigintOverSmallint | Prefer BIGINT over SMALLINT types. | |
| preferIdentity | Prefer using IDENTITY columns over serial columns. | |
| preferJsonb | Prefer JSONB over JSON types. | |
| preferRobustStmts | Prefer statements with guards for robustness in migrations. | |
| preferTextField | Prefer using TEXT over VARCHAR(n) types. | |
| preferTimestamptz | Prefer TIMESTAMPTZ over TIMESTAMP types. | |
| renamingColumn | Renaming columns may break existing queries and application code. | |
| renamingTable | Renaming tables may break existing queries and application code. | |
| requireConcurrentDetachPartition | Detaching a partition without CONCURRENTLY acquires an ACCESS EXCLUSIVE lock. |
✅ |
| requireConcurrentIndexCreation | Creating indexes non-concurrently can lock the table for writes. | |
| requireConcurrentIndexDeletion | Dropping indexes non-concurrently can lock the table for reads. | |
| requireConcurrentRefreshMatview | REFRESH MATERIALIZED VIEW without CONCURRENTLY acquires an ACCESS EXCLUSIVE lock. |
✅ |
| requireConcurrentReindex | REINDEX without CONCURRENTLY acquires an ACCESS EXCLUSIVE lock on the table. |
✅ |
| requireIdleInTransactionTimeout | Dangerous lock statements should be preceded by SET idle_in_transaction_session_timeout. |
|
| requireSeparateConstraintValidation | Validating a constraint in the same transaction it was added as NOT VALID defeats the purpose. |
✅ |
| requireStatementTimeout | Dangerous lock statements should be preceded by SET statement_timeout. |
|
| runningStatementWhileHoldingAccessExclusive | Running additional statements while holding an ACCESS EXCLUSIVE lock blocks all table access. | ✅ |
| transactionNesting | Detects problematic transaction nesting that could lead to unexpected behavior. |