How to Fix PostgreSQL 'relation pg_stat_statements does not exist' Error
Threat/Impact Level: MEDIUM | Downtime Risk: HIGH (monitoring blind spot, query performance regression undetectable) | Time to Fix: 5 mins
TL;DR
- What broke:
pg_stat_statementsis referenced in a query or monitoring tool, but the extension was never created in the current database withCREATE EXTENSION. - How to fix it: Add
pg_stat_statementstoshared_preload_librariesinpostgresql.conf, restart Postgres, then runCREATE EXTENSION pg_stat_statements;as a superuser in the target database. - Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your
postgresql.confor failing migration SQL and get corrected output instantly.
▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]
The Incident (What Does the Error Mean?)
Raw error output from psql or application logs:
ERROR: relation "pg_stat_statements" does not exist
LINE 1: SELECT * FROM pg_stat_statements ORDER BY total_exec_time DESC;
^Immediate consequence: Every query hitting pg_stat_statements — your APM agent, pgBadger, Datadog postgres integration, custom slow-query dashboards — returns a hard error. You are now flying blind on query performance. In a production incident, this means you cannot identify the offending query causing CPU spike or lock contention.
This is not a transient error. It will not self-heal. The view does not exist because the extension lifecycle was never completed.
▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]
The Attack Vector / Blast Radius
This is a monitoring gap, not a security exploit — but the blast radius is significant:
- Observability blackout: No
pg_stat_statementsmeans no per-query execution stats. You losecalls,total_exec_time,rows,shared_blk_hit— the entire query performance fingerprint. - Cascading alerting failures: PgBouncer health checks, Datadog
postgres.queries.countmetrics, and custom Grafana panels all start returning errors or zeroes. On-call engineers see false positives. - Post-incident forensics destroyed: If you hit a slow-query incident and this extension was never loaded, you have no historical execution data to do RCA.
- Multi-database trap: Even if
shared_preload_librariesis set correctly,CREATE EXTENSIONis per-database. A new database provisioned fromtemplate1will silently lack the extension unless your provisioning automation handles it.
How to Fix It
Step 1 — Preload the Module (requires restart)
postgresql.conf:
- #shared_preload_libraries = ''
+ shared_preload_libraries = 'pg_stat_statements'If other libraries are already loaded:
- shared_preload_libraries = 'pg_partman_bgw'
+ shared_preload_libraries = 'pg_partman_bgw,pg_stat_statements'Restart Postgres. On systemd:
systemctl restart postgresqlOn RDS/Aurora: modify the parameter group, set shared_preload_libraries to include pg_stat_statements, and reboot the instance.
Step 2 — Create the Extension in the Target Database
Connect as superuser to each database that needs it:
- -- Extension was never created
- SELECT * FROM pg_stat_statements;
+ -- Run this once per database as superuser
+ CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+ SELECT * FROM pg_stat_statements LIMIT 5;Step 3 — Tune Collection Parameters (Enterprise Best Practice)
Default pg_stat_statements.max is 5000 — too low for high-throughput OLTP. Add to postgresql.conf:
- # No pg_stat_statements tuning
+ pg_stat_statements.max = 10000
+ pg_stat_statements.track = all
+ pg_stat_statements.track_utility = on
+ pg_stat_statements.save = onGrant read access to a non-superuser monitoring role (least privilege):
- -- Monitoring agent connecting as superuser
+ GRANT pg_monitor TO monitoring_user;
+ GRANT SELECT ON pg_stat_statements TO monitoring_user;💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.
(For CI/CD pipeline automation, install via
npm i -g stackengine).
▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]
Prevention in CI/CD
1. Enforce Extension in Database Migrations
Add this as the first migration in Flyway, Liquibase, or raw SQL migration scripts:
-- V001__baseline_extensions.sql
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;The IF NOT EXISTS guard makes it idempotent — safe to re-run.
2. Terraform / Helm Enforcement
If provisioning RDS via Terraform, enforce the parameter group:
- # No parameter group defined — using default
+ resource "aws_db_parameter_group" "postgres" {
+ family = "postgres15"
+ parameter {
+ name = "shared_preload_libraries"
+ value = "pg_stat_statements"
+ apply_method = "pending-reboot"
+ }
+ }3. Checkov / OPA Policy Gate
Write a custom Checkov check that fails any Terraform plan where an aws_db_instance resource does not reference a parameter group containing pg_stat_statements. Block the PR before it reaches staging.
4. Post-Deploy Smoke Test
Add to your deployment pipeline's health check stage:
psql $DATABASE_URL -c "SELECT count(*) FROM pg_stat_statements;" || (echo 'pg_stat_statements missing' && exit 1)This turns a silent monitoring gap into a hard pipeline failure.
Official Grounding & Normative Specifications
The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:
Official runtime configuration documentation governing lock acquisition timeouts, deadlock detection cycles, and wait logging.
Federal security guidelines for secret exfiltration prevention, automated integrity checks, and peer sign-offs.
Core memory subsystem controls, pressure stall information (PSI), and kernel OOM invocation mechanics.
Industry-standard specification for container execution boundaries and POSIX namespace isolation.