# pg_plan_filter 1.0 lets PostgreSQL reject expensive query plans — but not hostile users

The new pg_plan_filter 1.0 module can stop individual SQL statements or whole transactions when PostgreSQL's estimated plan cost crosses a configured ceiling. Its own documentation warns that session-controlled planner settings can defeat it, so it is an operational guard rather than a security boundary.

PostgreSQL operators gain per-statement and per-transaction estimated-cost limits across versions 14–18, useful for runaway reports and ORMs. The guard is disabled by default and can be bypassed by users allowed to change planner cost parameters.

- Status: Active
- Published: 2026-10-09T06:22:22+13:00
- Updated: 2026-10-09T06:22:22+13:00
- Categories: Web Development, Cloud & Infrastructure, Developer Tools, Databases & Storage
- Tags: database operations, open source, PostgreSQL
- Canonical HTML: https://beyondthe.news/dossiers/pg-plan-filter-1-postgresql-statement-transaction-cost-guard

## What changed

PGX, Inc. announced pg_plan_filter 1.0.0 on October 7, 2026. The PostgreSQL loadable module checks a statement's estimated execution plan cost before allowing it to run. Administrators can set `plan_filter.statement_cost_limit` to reject a single expensive plan, `plan_filter.transaction_cost_limit` to cap the sum of estimated costs within one transaction, and `plan_filter.filter_select_only` to scope filtering to SELECT. Both limits default to zero (disabled). The module supports PostgreSQL 14 through 18 and can be loaded with `shared_preload_libraries`. Its upstream README documents important limits: estimates can be inaccurate, DDL without plans is not counted, and users who can alter planner cost settings can artificially lower estimates and evade the limits.

## Why it matters

Small teams often discover an unexpectedly expensive query only after it slows the entire database. An application report, ORM-generated query or batch of individually modest statements can consume disproportionate capacity. pg_plan_filter adds a configurable refusal point before execution, including a cumulative transaction budget rather than only a timeout. That is a useful safety layer for cooperative workloads and self-hosted PostgreSQL, but it is not a billing cap, an actual resource meter or a defence against an adversarial SQL user. Its most important operational design decision is whether the workload trusts the session's planner settings.

## Two budgets address different failure patterns

`statement_cost_limit` rejects one plan whose estimated cost exceeds the configured value. `transaction_cost_limit` adds estimated costs across executed statements in the same transaction, including repeated prepared-statement executions, so a batch of cheap-looking statements cannot grow indefinitely without tripping a cumulative threshold. The extension returns SQLSTATE `54001` on rejection, which applications can handle deliberately rather than treating every rejection as a generic database outage.

## It measures the planner, not CPU seconds

PostgreSQL's cost units are planner estimates, not elapsed time, bytes scanned or money spent. A poorly estimated query may slip through; an efficient query may be refused. Operators should tune limits against real `EXPLAIN` plans and workload traces and expect false positives. Plain `EXPLAIN` can itself be affected by a nonzero statement limit unless the setting is temporarily disabled by an authorized user.

## Do not mistake the module for a security boundary

The module's own maintainers explicitly warn that planner cost coefficients such as `seq_page_cost` and `cpu_tuple_cost` are session-settable. A user able to issue `SET` can lower those coefficients, even to zero, and make a dangerous plan appear cheap without permission to change the filter's superuser-only thresholds. Treat the filter as a guard against accidents or careless workloads, not a defence against malicious roles. Keep application-level rate limits, query privileges and infrastructure limits in place.

## Deployment is an operator task

pg_plan_filter is a loadable module, not a normal `CREATE EXTENSION` installable package. The project recommends `shared_preload_libraries` for production and supports PostgreSQL 14–18. Administrators can apply different limits per role, keep privileged maintenance accounts unrestricted and test rejection handling before rolling it into an application. Most DDL without an execution plan is outside the budget.

## Key details

- pg_plan_filter 1.0.0 was announced by PGX on October 7, 2026.
- Supported PostgreSQL major versions are 14–18.
- Per-statement estimated-cost threshold: `plan_filter.statement_cost_limit`.
- Per-transaction cumulative estimated-cost threshold: `plan_filter.transaction_cost_limit`.
- Optional SELECT-only filtering; SELECT is not necessarily read-only.
- Both cost limits default to zero, meaning filtering is off until configured.
- Rejections raise SQLSTATE 54001 (`statement_too_complex`).
- Production deployment is via a loadable module, preferably `shared_preload_libraries`, not `CREATE EXTENSION`.
- Planner cost coefficients are user-settable, making this a cooperative safety control rather than a security boundary.

## Builder takeaways

- Use the module to reduce accidental database overload from reports, ORMs and batch interfaces, not to police hostile SQL users.
- Measure representative production query plans before setting cost ceilings, and leave headroom for legitimate expensive work.
- Test application behaviour when SQLSTATE 54001 is returned and give users a useful error.
- Combine it with timeouts, least-privilege roles, rate limits and resource monitoring; cost estimates do not measure actual resource use.
- Keep maintenance and migration roles appropriately configured, and remember that most DDL is not counted.

## What to watch

- Whether the module gains hardened limits that are not bypassable through session planner settings.
- PostgreSQL 19 compatibility and packaging/distribution support.
- Evidence from production deployments on false positives and useful thresholds.
- Broader cost-aware workload governance in PostgreSQL extensions.

## Uncertainties

- Estimated plan cost is workload- and configuration-dependent, not a universal CPU or latency metric.
- Upstream documentation explicitly warns of bypasses by roles able to change planner cost coefficients.
- No independent production benchmark or failure-rate dataset was published with the 1.0 announcement.

## Sources

- [pg_plan_filter 1.0.0 released](https://www.postgresql.org/about/news/pg_plan_filter-100-released-3352/) — PostgreSQL News / PGX · primary project announcement · 2026-10-07T00:00:00+13:00. Version 1.0 release, statement/transaction cost limits and supported PostgreSQL versions.
- [pg_plan_filter upstream README](https://github.com/pgexperts/pg_plan_filter) — PostgreSQL Experts · primary repository · 2026-10-09T00:00:00+13:00. Detailed configuration, transaction accounting, SQLSTATE, installation and crucial bypass/security warnings.

