Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1igoW4-00016d-JG for pgsql-docs@arkaria.postgresql.org; Mon, 16 Dec 2019 11:26:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1igoW3-00064X-7K for pgsql-docs@arkaria.postgresql.org; Mon, 16 Dec 2019 11:26:19 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1igoW2-00064Q-T3 for pgsql-docs@lists.postgresql.org; Mon, 16 Dec 2019 11:26:19 +0000 Received: from mail-wr1-x442.google.com ([2a00:1450:4864:20::442]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1igoVw-0005hf-2E for pgsql-docs@lists.postgresql.org; Mon, 16 Dec 2019 11:26:18 +0000 Received: by mail-wr1-x442.google.com with SMTP id y17so6793170wrh.5 for ; Mon, 16 Dec 2019 03:26:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=4QiBE3ndvMPWZFDNtRAL+5fW6rmBkX7JUMfjxDCnaOQ=; b=tgtNdAWXbpJbGNpcsqagVmch6G0VDlK8b1uH1WeMGKnGFte4Vcs/hMR9DKrRBwBXi2 KO2wwYWw6Ybfkg8J9PStkoenjVS0xd3Tg9hhEXqW6QBGhyvyP7OBa+XY3KxWaCwDjpaN q+PfeymLnhyu0r7m1sahmZCJ44V2fyGpuWZ1Yxm0qiu6SEVWsXmmxBZ9QMeS2C5qJSYw x96o9kZpjIQ03PcQRnDK2yJ49VqQtX0yY6HTw+m20GbuhOttP4PraONhFBwz8NGTLP4Z aGUmScG6+8L+ETNpKuwb5BomZdEYI/psmvo66j8c0CtiWudvxZXka0KPHw7/9rrXwep6 AAyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=4QiBE3ndvMPWZFDNtRAL+5fW6rmBkX7JUMfjxDCnaOQ=; b=NcF1wxlkUNWaLImpEeMzJjDK/t/SE8NTJJ+4O2UypmmyOGmQeTb3mmEnWqkU1xHYLZ J5VLqP1t+yP8iuQwGGqf1y1engacQM78ym0FQ5SzFk5AvqIryqeW1lKaJ13MU3MtNQdh 6ANytlurGVEwLgnn/tQUz8wmzAp90UzG1EdY1gU9QjMTtOEX9FItJtyYRP9kLkWLA2S4 Nd4ZYNOViEuJXmCzooKMx/y+QMmXgoifVix5SXCtwYpK95FbbT4LHUqKfgWRTqur+kkc ACXrjhnuSbeiwyJgC8QR2RJEFzwu4rg/A8AB5kMXRD36AQnwtEnN2+0oS/GzwWlW/dz4 107A== X-Gm-Message-State: APjAAAWsfQPICx/tBwi/dCyBUjGMqcQGkGJS2ulBSCJvYWIeurfwcnro LMjaUDU+ZPy3ao/QXVocqafgr0ayLswngxtZdsTaT87v X-Google-Smtp-Source: APXvYqxdbNrWtNcCv7M7NLVQWj6onO+N7ZJZV0Xrrb0vIPuYHpEkuINHz5wGdo7BnJ70XcAjAsTYEkh3tqXyvzSTWzo= X-Received: by 2002:a05:6000:11c6:: with SMTP id i6mr30219035wrx.178.1576495571138; Mon, 16 Dec 2019 03:26:11 -0800 (PST) MIME-Version: 1.0 References: <157644931740.18138.16968232991166819127@wrigleys.postgresql.org> In-Reply-To: <157644931740.18138.16968232991166819127@wrigleys.postgresql.org> From: Pavel Stehule Date: Mon, 16 Dec 2019 12:25:34 +0100 Message-ID: Subject: Re: Example 42.4. A PL/pgSQL Trigger Function for Auditing To: simon.dallaway@datacom.co.nz, pgsql-docs@lists.postgresql.org Content-Type: multipart/alternative; boundary="00000000000081fc0e0599d07a1d" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --00000000000081fc0e0599d07a1d Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable po 16. 12. 2019 v 12:12 odes=C3=ADlatel PG Doc comments form < noreply@postgresql.org> napsal: > The following documentation comment has been logged on the website: > > Page: https://www.postgresql.org/docs/12/plpgsql-trigger.html > Description: > > I'm wondering if it would be worthwhile to put a totally generic auditing > function into the documentation e.g. > > CREATE OR REPLACE FUNCTION zz_audit_() RETURNS TRIGGER AS $nothing$ > > -- This function is intended to be used by a delete/insert/update trigger > for any table. > -- It relies on the existence of a table named zz_audit_XXX (where XXX is > the table being audited) that contains the > -- same columns as the table XXX except that two additional columns must > exist prior to the columns from XXX > -- operation character(1) NOT NULL, > -- tstamp timestamp with time zone NOT NULL, > -- ... remaining columns per table XXX > > DECLARE audit_table_name NAME :=3D CONCAT('zz_audit_', TG_TABLE_NAME)= ; > BEGIN > > IF (TG_OP =3D 'DELETE') THEN > EXECUTE 'INSERT INTO ' || audit_table_name || ' SELECT ''D'', > now(), ' || ' $1.*' USING OLD; > ELSIF (TG_OP =3D 'UPDATE') THEN > EXECUTE 'INSERT INTO ' || audit_table_name || ' > SELECT ''U'', now(), ' || > ' $1.*' USING NEW; > ELSIF (TG_OP =3D 'INSERT') THEN > EXECUTE 'INSERT INTO ' || audit_table_name || ' > SELECT ''I'', now(), ' || > ' $1.*' USING NEW; > END IF; > > RETURN NULL; -- result is ignored since this is an AFTER trigger > END; > $nothing$ LANGUAGE plpgsql; > Just few points to this code 1. bad, useless brackets in IF .. ELSIF expressions - plpgsql is not C or Java 2. unescaped identifiers in dynamic SQL - EXECUTE 3. there is not reason for INSERT SELECT. Regards Pavel --00000000000081fc0e0599d07a1d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
po 16. 12. 2019 v=C2=A012:12 odes=C3= =ADlatel PG Doc comments form <noreply@postgresql.org> napsal:
The following documentation comment has been logged= on the website:

Page: https://www.postgresql.org/docs/12/plpg= sql-trigger.html
Description:

I'm wondering if it would be worthwhile to put a totally generic auditi= ng
function into the documentation e.g.

CREATE OR REPLACE FUNCTION zz_audit_() RETURNS TRIGGER AS $nothing$

-- This function is intended to be used by a delete/insert/update trigger for any table.
-- It relies on the existence of a table named zz_audit_XXX (where XXX is the table being audited) that contains the
-- same columns as the table XXX except that two additional columns must exist prior to the columns from XXX
--=C2=A0 =C2=A0 operation character(1) NOT NULL,
--=C2=A0 =C2=A0 tstamp=C2=A0 =C2=A0 timestamp with time zone NOT NULL,
--=C2=A0 =C2=A0 ...=C2=A0 =C2=A0 =C2=A0 =C2=A0remaining columns per table X= XX

=C2=A0 =C2=A0 DECLARE audit_table_name NAME :=3D CONCAT('zz_audit_'= , TG_TABLE_NAME);
=C2=A0 =C2=A0 BEGIN

=C2=A0 =C2=A0 =C2=A0 =C2=A0 IF (TG_OP =3D 'DELETE') THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 EXECUTE 'INSERT INTO ' ||= audit_table_name || ' SELECT ''D'',
now(), ' || ' $1.*' USING OLD;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ELSIF (TG_OP =3D 'UPDATE') THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 EXECUTE 'INSERT INTO ' || audit_table_name || ' SELE= CT ''U'', now(), ' ||
' $1.*' USING NEW;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ELSIF (TG_OP =3D 'INSERT') THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 EXECUTE 'INSERT INTO ' || audit_table_name || ' SELE= CT ''I'', now(), ' ||
' $1.*' USING NEW;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 END IF;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 RETURN NULL; -- result is ignored since this is= an AFTER trigger
=C2=A0 =C2=A0 END;
$nothing$ LANGUAGE plpgsql;

Just few po= ints to this code

1. bad, useless brackets in IF .= . ELSIF expressions - plpgsql is not C or Java
2. unescaped ident= ifiers in dynamic SQL - EXECUTE
3. there is not reason for INSERT= SELECT.

Regards

Pavel
--00000000000081fc0e0599d07a1d--