https://github.com/stefanov-sm/postgres-generic-audit-trigger
PostgreSQL generic audit trigger of inserts, updates and deletes
https://github.com/stefanov-sm/postgres-generic-audit-trigger
Last synced: about 1 month ago
JSON representation
PostgreSQL generic audit trigger of inserts, updates and deletes
- Host: GitHub
- URL: https://github.com/stefanov-sm/postgres-generic-audit-trigger
- Owner: stefanov-sm
- License: mit
- Created: 2021-12-28T18:05:57.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-02-13T11:51:01.000Z (over 1 year ago)
- Last Synced: 2025-02-15T23:42:38.227Z (3 months ago)
- Language: PLpgSQL
- Size: 24.4 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Audit: audit-log.sql
Awesome Lists containing this project
README
# PostgreSQL generic audit trigger
### Generic audit log of inserts, updates and deletionsDefinitions in file [audit-log.sql](https://github.com/stefanov-sm/Postgres-generic-audit-trigger/blob/main/audit-log.sql).
Table `audit.audit` and functions `audit.key_values_as_jsonb`, `audit.audit_trigger_fn` and `audit.get_pk_keys_text` must be created before running the demo.
> [!NOTE]
> Function `audit.audit_trigger_fn` uses [JSONB subscripting](https://www.postgresql.org/docs/14/datatype-json.html#JSONB-SUBSCRIPTING) syntax, available since PG14. For older versions lines 55 - 57 in file `audit-log.sql` need to be rewritten.
>
> Non identifier-friendly names ("Drop me", "Is it good") are used deliberately**Demo**
```sql
-- drop schema if exists tests cascade;
-- create schema tests;-- drop table if exists tests."Drop me"
create table tests."Drop me"
(
id serial not null,
"Is it good" boolean not null default true,
ts timestamp with time zone not null default now(),
uname text,
description text,
amount numeric,
constraint pk_drop_me primary key (id, "Is it good")
);-- truncate table tests."Drop me";
-- truncate table audit.audit;select format(
'create or replace trigger %I
after delete or update -- or insert
on %I.%I for each row
execute function audit.audit_trigger_fn(%L);',
:trigger_name,
:schema_name,
:table_name,
audit.get_pk_keys_text(:schema_name, :table_name)
);-- Create trigger. This is the output of the query above with parameter values 'drop_me_audit_trigger', 'tests' and 'Drop me'.
create or replace trigger drop_me_audit_trigger
after delete or update -- or insert
on tests."Drop me" for each row
execute function audit.audit_trigger_fn('{id,"Is it good"}');insert into tests."Drop me" (uname, description, amount)
values ('Rita', 'dog', 1), ('Луция', 'cat', 2), ('Pesho', 'hamster', 3);insert into tests."Drop me" (uname, description, amount)
values ('Stefan', 'human male', 4), ('Lili', 'human female', 5), ('Bubu', 'vacuum cleaner robot', 6);select * from tests."Drop me";
update tests."Drop me" set uname = 'Lucia' where description = 'cat';
update tests."Drop me" set uname = 'Lucia' where description = 'cat'; -- nothing changes
delete from tests."Drop me" where description ~* 'robot';select * from audit.audit;
```
|ts|schema_name|table_name|user_name|operation|payload|row_ident|
|--|-----------|----------|---------|---------|-------|---------|
|2024-02-13 12:17:44.077 +0200|tests|Drop me|some_user|UPDATE|{"new": {"uname": "Lucia"}, "old": {"uname": "Луция"}}|{"id": 2, "Is it good": true}|
|2024-02-13 12:17:45.399 +0200|tests|Drop me|some_user|UPDATE|{}|{"id": 2, "Is it good": true}|
|2024-02-13 12:17:46.424 +0200|tests|Drop me|some_user|DELETE|{"old": {"id": 6, "ts": "2024-02-13T12:17:38.647511+02:00", "uname": "Bubu", "amount": 6, "Is it good": true, "description": "vacuum cleaner robot"}}||