pg_savior
pg_savior : Postgres extension to save OOPS mistakes
Overview
| ID | Extension | Package | Version | Category | License | Language |
|---|---|---|---|---|---|---|
| 5810 | pg_savior
|
pg_savior
|
0.1.0 |
ADMIN
|
Apache-2.0
|
C
|
| Attribute | Has Binary | Has Library | Need Load | Has DDL | Relocatable | Trusted |
|---|---|---|---|---|---|---|
--s-d-r
|
No
|
Yes
|
No
|
Yes
|
yes
|
no
|
| Relationships | |
|---|---|
| See Also | pg_upless
safeupdate
pg_drop_events
pg_cheat_funcs
table_log
pg_snakeoil
pg_auditor
temporal_tables
|
-tuplestore_donestoring , breaks on pg18 @ el
Packages
| Type | Repo | Version | PG Major Compatibility | Package Pattern | Dependencies |
|---|---|---|---|---|---|
| EXT | PIGSTY
|
0.1.0 |
18
17
16
15
14
|
pg_savior |
- |
| RPM | PIGSTY
|
0.1.0 |
18
17
16
15
14
|
pg_savior_$v |
- |
| DEB | PIGSTY
|
0.1.0 |
18
17
16
15
14
|
postgresql-$v-pg-savior |
- |
| Linux / PG | PG18 | PG17 | PG16 | PG15 | PG14 |
|---|---|---|---|---|---|
el8.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
el8.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
el9.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
el9.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
el10.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
el10.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
d12.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
d12.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
d13.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
d13.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u22.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u22.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u24.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u24.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u26.x86_64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
u26.aarch64
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
PIGSTY 0.1.0
|
Source
pig build pkg pg_savior; # build rpm/debInstall
Make sure PGDG and PIGSTY repo available:
pig repo add pgsql -u # add both repo and update cacheInstall this extension with pig:
pig install pg_savior; # install via package name, for the active PG version
pig install pg_savior -v 18; # install for PG 18
pig install pg_savior -v 17; # install for PG 17
pig install pg_savior -v 16; # install for PG 16
pig install pg_savior -v 15; # install for PG 15
pig install pg_savior -v 14; # install for PG 14Create this extension with:
CREATE EXTENSION pg_savior;Usage
Sources: README, release 0.1.0, PGXN 0.1.0, SQL file, C source, pg_savior.control
pg_savior is a PostgreSQL safety extension for blocking specific high-risk DML and DDL statements before they run. Version 0.1.0 is a deliberate PGXN release and a major rewrite from 0.0.1; the README still labels it pre-1.0 and not production-ready.
Activation
CREATE EXTENSION alone does not activate the checks. The SQL file only documents that protection lives in the loaded shared library, so each backend must load pg_savior by one of the upstream-supported paths:
Cluster-wide activation uses shared_preload_libraries and requires a PostgreSQL restart:
shared_preload_libraries = 'pg_savior'Per-session activation for new connections can use session_preload_libraries after a config reload:
session_preload_libraries = 'pg_savior'For development or test sessions, load the library manually:
LOAD 'pg_savior';
CREATE EXTENSION pg_savior;Once the library is loaded, _PG_init installs post_parse_analyze_hook, ExecutorStart_hook, and ProcessUtility_hook for that backend.
DML Guards
pg_savior blocks DELETE and UPDATE statements that have no WHERE clause. The parser hook checks the analyzed Query tree and raises ERROR, so the transaction aborts and the application sees the failure.
CREATE TABLE emp (id int);
INSERT INTO emp VALUES (1), (2), (3);
DELETE FROM emp;
-- ERROR: pg_savior: DELETE without WHERE clause is blocked
UPDATE emp SET id = id + 1;
-- ERROR: pg_savior: UPDATE without WHERE clause is blocked
DELETE FROM emp WHERE id = 1;
-- allowedThe optional row-count guard applies to DELETE and UPDATE statements whose planner estimate exceeds pg_savior.max_rows_affected. It runs from ExecutorStart_hook, after planning and before tuples are touched.
SET pg_savior.max_rows_affected = 100;
DELETE FROM emp WHERE id > 0;
-- blocked if the planner estimate is greater than 100 rowsDDL Guards
The ProcessUtility_hook guards only the DDL operations listed by upstream:
CREATE INDEXwithoutCONCURRENTLYis always blocked.DROP DATABASEis always blocked.ALTER TABLE ADD COLUMN ... DEFAULTis blocked when the target table is larger thanpg_savior.large_table_threshold_rows.ALTER TABLE ALTER COLUMN TYPEis blocked for large tables.TRUNCATEis blocked when any target table is large.DROP TABLEis blocked when any target table is large.
Large-table checks use pg_class.reltuples > pg_savior.large_table_threshold_rows.
CREATE INDEX emp_idx ON emp (id);
-- ERROR: pg_savior: CREATE INDEX without CONCURRENTLY is blocked
CREATE INDEX CONCURRENTLY emp_idx ON emp (id);
-- allowed by this guard
ALTER TABLE big_emp ADD COLUMN status text DEFAULT 'active';
-- blocked when big_emp is over pg_savior.large_table_threshold_rows
TRUNCATE big_emp;
-- blocked when big_emp is over pg_savior.large_table_threshold_rowsConfiguration
All documented GUCs are session-scoped USERSET variables:
| GUC | Default | Effect |
|---|---|---|
pg_savior.enabled |
on |
Master switch; when off, checks do not run. |
pg_savior.bypass |
off |
Allows the current session through the guards. |
pg_savior.max_rows_affected |
0 |
Blocks estimated DELETE/UPDATE row counts above this value; 0 disables the check. |
pg_savior.large_table_threshold_rows |
1000000 |
Defines “large” for the guarded large-table DDL operations. |
Use SET LOCAL for a deliberate one-transaction bypass:
BEGIN;
SET LOCAL pg_savior.bypass = on;
DELETE FROM staging_table;
COMMIT;Caveats
- The library must be loaded in the backend before protection exists;
CREATE EXTENSION pg_savioronly registers extension metadata. - The row-count and large-table guards depend on planner/catalog estimates. Run
ANALYZEwhen recent changes make estimates stale. UPDATEcoverage is limited to unguardedUPDATEand the optional planner-estimate threshold; the README does not claim semantic review of everyWHEREpredicate.- DDL coverage is limited to the listed
ProcessUtility_hookcases. Do not assume other schema changes are blocked. - The
ADD COLUMN ... DEFAULTguard is conservative and blocks any default on a large table, including non-volatile defaults that newer PostgreSQL versions may handle without a full table rewrite.