Linting¶
The language server provides static analysis through linting rules that detect potential issues in your SQL code. The linter analyses SQL statements for safety issues, best practices violations, and problems that could break existing applications.
Rules¶
Every rule has a flat name, like banDropColumn, and belongs to a group: correctness, safety, destructive, style, typecheck, or nursery. Rules can be configured individually, and groups in bulk.
See the Rules Reference for the complete list of available rules and their descriptions.
Configuration¶
Configure linting behavior in your postgres-language-server.jsonc:
{
"linter": {
// Enable/disable the linter entirely
"enabled": true,
"rules": {
// Individual rule configuration: error, warn, info, hint, off
"banDropColumn": "error",
"banDropTable": "warn",
"addingRequiredField": "off"
},
// Configure whole groups
"groups": {
"style": "off"
}
}
}
The former nested form "rules": { "safety": { ... } } still works, but prints a deprecation warning. See Configuration for the details.
Suppressing Diagnostics¶
You can suppress specific diagnostics using comments:
-- pgls-ignore banDropColumn: Intentionally dropping deprecated column
ALTER TABLE users DROP COLUMN deprecated_field;
-- pgls-ignore banDropTable: Cleanup during migration
DROP TABLE temp_migration_table;
For more details on suppressions check out our guide.
Schema-Aware Analysis¶
Some rules require a database connection to perform schema-aware analysis, like the rules of the typecheck group described in Type Checking. If no connection is configured, they are skipped.
CLI Usage¶
The linter can also be used via the CLI for CI integration:
# Lint specific files
postgres-language-server check migrations/
Which rules run is set in the configuration file, see above.
See the CLI Reference for more options, and check the guide on linting migrations.
Autofix¶
Some rules can fix the issues they report. The fixes come in two kinds:
- Safe fixes only do what the rule asks for, e.g. add
CONCURRENTLYtoREINDEX(requireConcurrentReindex). They are left out where Postgres would reject them.CONCURRENTLYcan't run in a transaction block or in a function body, so the safe fix only adds it where the statement surely runs outside a transaction: in a migration section without a transaction (-- migrate:up transaction:falsefor dbmate, if the statement is the only one of the section, or-- +goose NO TRANSACTIONfor goose, see Checking migrations). Without section markers, we don't know whether the migration tool runs a file in a transaction. Migration tools usually do, so a file with more than one statement gets no fix, and addingCONCURRENTLYto a file with a single statement is an unsafe fix. It is never added on partitioned tables and system catalogs, which Postgres can't process concurrently. - Unsafe fixes may change what a statement does, e.g. swap the type of a column or add
NOT VALIDto a constraint. Review them before applying them.
In the editor, the fixes are offered as quick fixes on the diagnostic, next to a quick fix that suppresses the rule for the statement with a -- pgls-ignore <rule> comment.
The source.fixAll.pgls code action applies all safe fixes of a file. To run it on save in VS Code:
{
"editor.codeActionsOnSave": {
"source.fixAll.pgls": "explicit"
}
}
On the command line, check --fix applies the safe fixes and writes the files. Add --unsafe to also apply the unsafe fixes:
postgres-language-server check --fix migrations/
postgres-language-server check --fix --unsafe migrations/
Diagnostics with a fix are tagged FIXABLE. The summary says how many diagnostics have a fix, and with --fix, how many unsafe fixes it skipped. --only and --skip limit the check, and the fixes, to some rules or groups:
postgres-language-server check --fix --only=requireConcurrentIndexCreation migrations/
With --stdin-file-path, check reads the SQL from stdin and prints it to stdout, with the fixes applied if --fix is set. The diagnostics go to stderr:
cat migration.sql | postgres-language-server check --fix --unsafe --stdin-file-path=migration.sql
The rules reference marks the rules with a fix with 🔧, and the page of each rule says whether its fix is safe. Most rules with fixes, like requireConcurrentIndexCreation, preferRobustStmts or preferTimestamptz, aren't in the recommended preset (✅), so enable them first:
{
"linter": {
"rules": {
"requireConcurrentIndexCreation": "warn",
"requireConcurrentIndexDeletion": "warn"
}
}
}
Rules marked 🛠in the reference, like the CONCURRENTLY rules, only run on files in migrations.migrationsDir if it is set.