PostgreSQL schema & query advisor — rule-based, no AI
Project description
pg-advisor
PostgreSQL schema & query advisor — rule-based, no AI, fully deterministic.
Connect your database, and it will tell you what's wrong and how to fix it.
╭──────────────────────────── pg-advisor Report ────────────────────────────╮
│ localhost/mydb │
│ ❌ 3 critical ⚠ 4 warnings ℹ 5 info (12 total) │
╰────────────────────────────────────────────────────────────────────────────╯
━━━ Table: orders ━━━
❌ CRITICAL FK_WITHOUT_INDEX.user_id
'orders.user_id' has a foreign key to 'users' but no index — JOINs will be slow.
→ CREATE INDEX idx_orders_user_id ON orders(user_id);
❌ CRITICAL FLOAT_FOR_MONEY.total
'orders.total' uses FLOAT type — precision errors may occur.
→ ALTER TABLE orders ALTER COLUMN total TYPE NUMERIC(12,2);
⚠ WARNING DUPLICATE_INDEX
2 indexes exist for columns ['total'] — one is redundant.
→ DROP INDEX idx_orders_total2;
Install
pip install pg-advisor
Quick Start
Run this after installing to check your PostgreSQL extension setup:
pg-advisor setup postgresql://user:pass@localhost:5432/mydb
Then run the full analysis:
pg-advisor analyze postgresql://user:pass@localhost:5432/mydb
Extension Setup
pg-advisor uses three PostgreSQL extensions. Run pg-advisor setup to check which ones are available on your database.
| Extension | Type | Used for | Required? |
|---|---|---|---|
pg_stat_activity |
Built-in | Live query monitoring, lock waits, connection pool | Auto-available |
pg_stat_statements |
Built-in, off by default | Slow queries, SELECT * detection, high-frequency queries | Optional |
hypopg |
Third-party | Confirms an index will actually improve query cost | Optional |
Missing extensions are skipped gracefully — pg-advisor will still run and report all other issues.
Enabling pg_stat_statements
-- 1. Add to postgresql.conf:
shared_preload_libraries = 'pg_stat_statements'
-- 2. Restart PostgreSQL, then run in your database:
CREATE EXTENSION pg_stat_statements;
Installing hypopg
# Ubuntu / Debian
sudo apt install postgresql-<version>-hypopg
# macOS (Homebrew)
brew install hypopg
-- Then in your database:
CREATE EXTENSION hypopg;
Usage
Option 1 — Direct URL:
pg-advisor analyze postgresql://user:pass@localhost:5432/mydb
Option 2 — Environment variable:
export DATABASE_URL=postgresql://user:pass@localhost/mydb
pg-advisor analyze
Option 3 — .env file:
DATABASE_URL=postgresql://user:pass@localhost/mydb
pg-advisor analyze
Scan model files too (SQLAlchemy / Django / SQL):
pg-advisor analyze --models-path ./models/
pg-advisor analyze --models-path . # scan entire project
Skip specific checks:
pg-advisor analyze --skip-queries # skip pg_stat_statements checks
pg-advisor analyze --skip-hypopg # skip hypothetical index testing
pg-advisor analyze --skip-activity # skip live connection monitoring
Output & Reports
After every run, pg-advisor prints a colored summary to the terminal. It then prompts you to save a Markdown report:
──────────────────────────────────────────────────
Save Markdown (.md) report? (yes/no):
Answer yes and the file is written immediately:
✅ Report saved at: /your/project/pgadvisor_report/pg_advisor_report_20260408_143022.md
Skip the prompt with flags:
pg-advisor analyze ... --save-report # always save, no prompt
pg-advisor analyze ... --no-report # never save, no prompt (CI/CD)
Report folder & file naming
| Detail | Value |
|---|---|
| Folder | pgadvisor_report/ — auto-created in your current working directory on first run |
| Filename | pg_advisor_report_YYYYMMDD_HHMMSS.md |
| Example | pgadvisor_report/pg_advisor_report_20260408_143022.md |
| Encoding | UTF-8 |
The folder is created automatically — no setup needed. Each run produces a uniquely timestamped file; existing reports are never overwritten.
What the Markdown report contains
- Summary header — database, timestamp, critical / warning / info counts
- Issue summary table — every issue across all tables in one place
- Per-table sections — each issue with severity, message, and a ready-to-run SQL fix block
- Rule reference — full list of all rules and what they check
What does it check?
Schema Rules
| Rule | What it detects | Severity |
|---|---|---|
MISSING_PK |
Table without primary key | ❌ Critical |
FLOAT_FOR_MONEY |
price, balance, total columns using FLOAT type |
❌ Critical |
FK_WITHOUT_INDEX |
No index on foreign key column | ❌ Critical |
NULLABLE_PK |
Primary key marked as nullable | ❌ Critical |
MISSING_CREATED_AT |
created_at column is missing |
⚠ Warning |
BOOL_AS_INT |
is_active, has_access stored as INTEGER |
⚠ Warning |
GOD_TABLE |
Table with 30+ columns — consider splitting | ⚠ Warning |
MISSING_NOT_NULL |
Email, name, status columns are nullable | ℹ Info |
MISSING_UPDATED_AT |
updated_at column is missing |
ℹ Info |
Index Rules
| Rule | What it detects | Severity |
|---|---|---|
DUPLICATE_INDEX |
2+ indexes on the same columns | ⚠ Warning |
UNUSED_INDEX |
Index never used (detected from live DB) | ⚠ Warning |
LOW_CARDINALITY_INDEX |
Index on boolean/status column | ℹ Info |
Query Rules (requires pg_stat_statements)
| Rule | What it detects | Severity |
|---|---|---|
SLOW_QUERY |
Average execution time ≥ 500ms | ❌ Critical |
HIGH_FREQUENCY_QUERY |
1000+ calls — consider caching | ⚠ Warning |
SELECT_STAR |
Use of SELECT * in queries |
⚠ Warning |
Activity Rules (pg_stat_activity — built-in)
| Rule | What it detects | Severity |
|---|---|---|
LONG_RUNNING_QUERY |
Query running for 30s+ | ❌ Critical |
IDLE_IN_TRANSACTION |
Connection idle-in-transaction for 60s+ | ❌ Critical |
LOCK_WAIT |
Query blocked waiting for a lock | ❌ Critical |
CONNECTION_POOL_PRESSURE |
80%+ of max_connections in use | ⚠ Warning |
Hypothetical Index Rules (requires hypopg)
| Rule | What it detects | Severity |
|---|---|---|
HYPOPG_INDEX_CONFIRMED |
Index verified to reduce query cost | ❌ Critical |
Model File Scanning
Can detect issues from model files without connecting to the database:
SQLAlchemy:
class Order(Base):
__tablename__ = "orders"
total = Column(Float) # ← will flag FLOAT_FOR_MONEY
Django ORM:
class Order(models.Model):
total = models.FloatField() # ← same flag
Plain SQL:
CREATE TABLE orders (
total FLOAT -- ← same flag
);
Project Structure
pg_advisor/
├── connectors/
│ └── postgres.py # DB connection, URL resolver
├── collectors/
│ ├── db_schema.py # Fetch schema from live DB
│ └── model_scanner.py # Scan SQLAlchemy/Django/SQL files
├── analyzers/
│ ├── schema_rules.py # Schema issues (9 rules)
│ ├── index_rules.py # Index issues (3 rules)
│ ├── query_rules.py # Query issues (3 rules)
│ ├── activity_rules.py # Live monitoring (4 rules)
│ └── hypopg_rules.py # Hypothetical index testing (1 rule)
├── reporters/
│ ├── cli_reporter.py # Colored terminal output
│ └── md_reporter.py # Markdown report generator
└── cli.py # Entry point
Requirements
- Python 3.11+
- PostgreSQL 12+
psycopg2-binary,rich,python-dotenv(auto-installed via pip)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pg_advisor-0.2.0.tar.gz.
File metadata
- Download URL: pg_advisor-0.2.0.tar.gz
- Upload date:
- Size: 30.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a622ee3e67dc750106137ee6d9c5b2fec13723024cffcbf6c3b180779719b9b5
|
|
| MD5 |
e4409895ea4e3fe8da15cc0952649d62
|
|
| BLAKE2b-256 |
399621e04744c23eebb5f1d4db4433be674e72407e3c1631dbafac2cb9c78c8e
|
File details
Details for the file pg_advisor-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pg_advisor-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e497063053c2da35bd3adfbb6208c12b976d4f3675a015ba93f2b9d822f9b4d
|
|
| MD5 |
a21ac6d349627c16eaa98362ef8b0227
|
|
| BLAKE2b-256 |
ef6dd8bdde87d469525fce2baaa02a78e23e39c7bd3f97b14c664331dcd47581
|