.sql files using the same parser that runs inside PostgreSQL itself — no running database required.
Why pgvet?
SQL code quality is critical for application performance and correctness, but common issues often slip through:- Non-deterministic queries:
LIMITwithoutORDER BYreturns random results - Broken NULL handling:
NOT IN (SELECT ...)fails silently when the subquery returns NULLs - Dangerous operations:
UPDATEorDELETEwithoutWHEREaffects every row - Fragile queries:
SELECT *breaks when table schemas change - Type system pitfalls: Using
= NULLinstead ofIS NULL, ortimestampwithout timezone
Installation
Install pgvet via
go install in secondsQuickstart
Run your first analysis in under 5 minutes
Rules
Browse all 15 built-in rules
Key features
Real PostgreSQL parser
pgvet uses pg_query_go, which embeds the actual PostgreSQL parser. This means it understands the same SQL syntax that PostgreSQL does, including:- CTEs (Common Table Expressions)
- Window functions
DISTINCT ONFOR UPDATEwith locking clauses- Complex subqueries and JOINs
15 built-in rules
pgvet ships with 15 rules that catch the most common SQL anti-patterns:- Correctness issues (errors):
NOT INwith subqueries,= NULLcomparisons - Non-deterministic behavior (warnings):
LIMITwithoutORDER BY,DISTINCT ONwithout matchingORDER BY - Dangerous operations (warnings):
UPDATE/DELETEwithoutWHERE, missing column lists inINSERT - Performance pitfalls (warnings):
LIKEpatterns starting with%, missing lock strategies - Type system guidance (warnings):
char(n)vstext,timestampwithout timezone
No database required
Unlike tools that require a running PostgreSQL instance, pgvet works purely through static analysis. This means:- Fast: analyze hundreds of files in seconds
- Safe: no risk of modifying data or affecting database state
- Simple: works in CI, pre-commit hooks, or local development without setup
- Flexible: analyze SQL files from any environment or schema version
CI-friendly output
pgvet supports both human-readable text and machine-readable JSON output:0 for clean, 1 for issues found, 2 for errors.
What problems does pgvet solve?
Catching NULL-handling bugs
Catching NULL-handling bugs
The query
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM banned) returns zero rows when banned.user_id contains any NULL value — even if the user is not banned. This is PostgreSQL’s three-valued logic at work, and it’s a common source of production bugs.pgvet catches this pattern and suggests using NOT EXISTS instead, which handles NULLs correctly.Preventing non-deterministic results
Preventing non-deterministic results
SELECT id FROM users LIMIT 10 returns a random 10 rows on each execution unless the table happens to have a fixed physical order. Adding ORDER BY created_at makes the query deterministic.pgvet warns about LIMIT without ORDER BY (except for LIMIT 1, which is often used for existence checks).Avoiding schema coupling
Avoiding schema coupling
SELECT * FROM users breaks when someone adds, removes, or reorders columns in the table. Application code that expects a fixed column order will fail.pgvet flags SELECT * in the outermost query and encourages explicit column lists.Guarding against dangerous operations
Guarding against dangerous operations
UPDATE users SET status = 'inactive' without a WHERE clause updates every row in the table. This is rarely intentional but easy to do by accident.pgvet warns about UPDATE and DELETE statements without WHERE clauses.Next steps
Install pgvet
Get started by installing pgvet via
go installRun your first check
Follow the quickstart guide to analyze your first SQL file
Explore rules
Learn about all built-in rules and how to enable or disable them