Trouble with single trigger using UUID as key, should I use two triggers?

From: Jason Aleski <jason(dot)aleski(at)gmail(dot)com>
To: pgsql-sql(at)lists(dot)postgresql(dot)org
Subject: Trouble with single trigger using UUID as key, should I use two triggers?
Date: 2018-03-10 02:15:47
Message-ID: 456d6fa7-0ade-4ceb-195b-77c37818c02f@gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-sql

I'm running into a foreign key constraint when running a BEFORE trigger
for audit purposes.  My initial goal was to do this in one BEFORE
trigger, but I'm leaning towards executing using two different
triggers.  I'm seeking a second opinion!  My initial thought was
creating a trigger that runs before the data is written to the primary
table; specifically on updates, as I want to increment the version_count
before the data is written to the primary table.  Because the audit_id
(UUID generated) has not been written to the primary table, I get a
foreign key constraint problem when it tries to first write to the audit
table.  I understand the error is because audit_id doesn't exist in the
primary table.  If I were using a SERIAL/SEQUENCE, I could use the
NEXTVAL function; but I need to use UUID the key and audit_id.

Knowing that, I think my only option is to create an AFTER trigger for
the INSERT.  Then create a BEFORE trigger when doing UPDATES and
DELETES.  Before I go this path, I through I'd see if anyone has any
additional options I should consider; or would this be the suggested
path?  Thoughts?

-JA-

--Example INSERT
INSERT INTO departments (department_legacyid, department_name) VALUES
('ACT', 'Accounting');
INSERT INTO departments (department_legacyid, department_name) VALUES
('HRM', 'Human Resources');
INSERT INTO departments (department_legacyid, department_name) VALUES
('CSR', 'Customer Service');

--ERROR
ERROR: insert or update on table "departments_audit" violates foreign
key constraint "departments_audit_audit_id_fkey" DETAIL: Key
(audit_id)=(241451bd-796e-5add-95b0-18f6098bc08f) is not present in
table "departments". CONTEXT: SQL statement "INSERT INTO
departments_audit (audit_id, audit_lastaction, department_legacyid,
department_name ) VALUES (NEW.audit_id, 'INSERT',
NEW.department_legacyid, NEW.department_name )" PL/pgSQL function
process_departments_audit() line 10 at SQL statement SQL state: 23503

--Primary Table
CREATE TABLE IF NOT EXISTS departments (
  row_id                UUID NOT NULL DEFAULT
uuid_generate_v5(uuid_ns_dns(), 'location1.local'),
  row_created           TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT
CURRENT_TIMESTAMP,
  audit_id              UUID NOT NULL DEFAULT
uuid_generate_v5(uuid_ns_dns(), 'location1.local'),
  version_count         INTEGER NOT NULL DEFAULT 1,
  version_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT
CURRENT_TIMESTAMP,
  department_legacyid   VARCHAR(255) NOT NULL,
  department_name       VARCHAR(255) NOT NULL,
  PRIMARY KEY (row_id)
);

--Creating index on audit_ID to ensure there is only one unique record
in primary table
CREATE UNIQUE INDEX departments_ref_idx ON departments(audit_id);

--Audit Table
CREATE TABLE IF NOT EXISTS departments_audit (
  row_id                UUID NOT NULL DEFAULT
uuid_generate_v5(uuid_ns_dns(), 'location1.local'),
  row_created           TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT
CURRENT_TIMESTAMP,
  audit_id              UUID NOT NULL DEFAULT
uuid_generate_v5(uuid_ns_dns(), 'location1.local'),
  audit_lastaction      CHARACTER VARYING DEFAULT 'NOT DEFINED',
  version_count         INTEGER NOT NULL DEFAULT 1,
version_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT
CURRENT_TIMESTAMP,
  department_legacyid   VARCHAR(255) NOT NULL,
  department_name       VARCHAR(255) NOT NULL,
  PRIMARY KEY (row_id),
  FOREIGN KEY (audit_id) REFERENCES departments(audit_id)
);

-- Creating audit trigger for departments
CREATE OR REPLACE FUNCTION process_departments_audit() RETURNS TRIGGER
AS $departments_audit$
  BEGIN
    IF (TG_OP = 'DELETE') THEN
      --Nothing yet
    ELSEIF (TG_OP = 'UPDATE') THEN
      --Nothing yet
      NEW.version_count = OLD.version_count + 1;
    ELSEIF (TG_OP = 'INSERT') THEN
      INSERT INTO departments_audit (audit_id,
                                     audit_lastaction,
                                     department_legacyid,
                                     department_name
                                    )
                             VALUES (NEW.audit_id,
                                     'INSERT',
                                     NEW.department_legacyid,
                                     NEW.department_name
                                    );
    END IF;
    RETURN NULL;
  END;
$departments_audit$ LANGUAGE plpgsql;

--Applying trigger BEFORE data is written to primary table
CREATE TRIGGER department_audit
  BEFORE INSERT OR UPDATE OR DELETE ON departments
    FOR EACH ROW EXECUTE PROCEDURE process_departments_audit();

Browse pgsql-sql by date

  From Date Subject
Next Message abctevez 2018-03-16 13:42:20 does the notification of postgres appear in transaction completed order
Previous Message Stanton Schmidt 2018-03-08 20:17:58 Re: Query question