BEGIN;
INSERT INTO users (email) VALUES ('a@x.com');
INSERT INTO users (email) VALUES ('a@x.com'); -- unique violation
INSERT INTO audit (msg) VALUES ('done');
COMMIT;
Nothing: the transaction is aborted, the audit insert fails with
current transaction is aborted, and COMMIT performs a rollbackNothing is saved. After any error, PostgreSQL marks the transaction as aborted. It then rejects every further command until the transaction ends, so the audit insert fails too. COMMIT on an aborted transaction performs a rollback.
"The first user and the audit row; the duplicate is skipped" is wrong, because PostgreSQL does not skip a failed statement and carry on. To recover from an expected error and continue, use a SAVEPOINT and ROLLBACK TO SAVEPOINT.
Read more