Initializing Enclave...

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_statements is referenced in a query or monitoring tool, but the extension was never created in the current database with CREATE EXTENSION.
  • How to fix it: Add pg_stat_statements to shared_preload_libraries in postgresql.conf, restart Postgres, then run CREATE EXTENSION pg_stat_statements; as a superuser in the target database.
  • Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your postgresql.conf or failing migration SQL and get corrected output instantly.

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.


The Attack Vector / Blast Radius

This is a monitoring gap, not a security exploit — but the blast radius is significant:

  1. Observability blackout: No pg_stat_statements means no per-query execution stats. You lose calls, total_exec_time, rows, shared_blk_hit — the entire query performance fingerprint.
  2. Cascading alerting failures: PgBouncer health checks, Datadog postgres.queries.count metrics, and custom Grafana panels all start returning errors or zeroes. On-call engineers see false positives.
  3. 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.
  4. Multi-database trap: Even if shared_preload_libraries is set correctly, CREATE EXTENSION is per-database. A new database provisioned from template1 will 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 postgresql

On 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 = on

Grant 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 configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


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.

Related Diagnostics

"Part of the Performance Utility Matrix."

View all 219 Performance Tools →