5

I have the following query to triggers on all tables in schema public:

SELECT 'CREATE TRIGGER ' || tab_name|| '_if_modified_trg INSERT OR UPDATE OR DELETE ON  ' || tab_name|| ' FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ' AS trigger_creation_query
FROM (
   SELECT quote_ident(table_schema) || '.' || quote_ident(table_name) as  tab_name
   FROM information_schema.tables
   WHERE table_schema='public'
   ) AS foo;

And I know how to check if a trigger exists:

SELECT tgname
from pg_trigger
where not tgisinternal AND tgname='randomname'

But how can I check in the first query whether a trigger with the same name already exists - and skip creating it and just continue? Here is my solution but it doesn't work:

SELECT 'CREATE TRIGGER ' || tab_name|| '_if_modified_trg INSERT OR UPDATE OR DELETE ON  ' || tab_name|| ' FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ' AS trigger_creation_query
FROM (
   SELECT quote_ident(table_schema) || '.' || quote_ident(table_name) as tab_name
   FROM information_schema.tables
   WHERE table_schema='public'    
 ) AS foo
 WHERE tab_name||'if_modified_trg' NOT IN (
    SELECT tgname
    from pg_trigger
    where not tgisinternal );
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Avon Dones
  • 51
  • 1
  • 3
  • [How to check if trigger exists in PostgreSQL?](http://stackoverflow.com/q/33174638/1995738) – klin May 21 '17 at 11:19

2 Answers2

5

Using this you can check if the trigger exists and create it if not. Don't forget the last ";".

DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'randomname') THEN
        CREATE TRIGGER randomname  
        AFTER INSERT OR UPDATE OR DELETE ON randomtable
        FOR EACH ROW EXECUTE PROCEDURE randomfunction();
    END IF;
END
$$;

I hope this can help you.

kaavsoft
  • 51
  • 1
  • 3
0

You can use a DO statement or a plpgsql function to execute the trigger creation conditionally:

DO
$do$
BEGIN
   IF EXISTS (
       SELECT 1
       FROM   pg_trigger
       WHERE  NOT tgisinternal AND tgname = 'randomname'
       ) THEN
      -- do nothing
   ELSE
      -- create trigger
   END IF;
END
$do$

On closer inspection, the rest of your code also has various problems. Seems like you are trying to do this:

DO
$do$
DECLARE
   _tbl regclass;
   _trg text;
BEGIN
   FOR _tbl, _trg IN
      SELECT c.oid::regclass, relname || '_if_modified_trg'
      FROM   pg_class c
      JOIN   pg_namespace n ON n.oid = c.relnamespace
      WHERE  n.nspname = 'public'
      AND    c.relkind = 'r'  -- only regular tables
   LOOP
      IF EXISTS (
         SELECT 
         FROM   pg_trigger
         WHERE  tgname  = _trg
         AND    tgrelid = _tbl       -- check only for respective table
         ) THEN
         -- do nothing
      ELSE
         -- create trigger
         EXECUTE format(
            'CREATE TRIGGER %I
             BEFORE INSERT OR UPDATE OR DELETE ON %s
             FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func()'
           , _trg, _tbl::text
         );
      END IF;
   END LOOP;
END
$do$;

I use the system catalog pg_class instead of information_schema.tables for multiple reasons. Most importantly, it includes the oid of the table, which makes the check on pg_trigger simpler and less error-prone.

We can actually simplify further and check for existence of the trigger in the same query. Substantially faster, yet:

DO
$do$
DECLARE
   _tbl text;
   _trg text;
BEGIN
FOR _tbl, _trg IN
   SELECT c.oid::regclass::text, relname || '_if_modified_trg'
   FROM   pg_class        c
   JOIN   pg_namespace    n ON n.oid = c.relnamespace
   LEFT   JOIN pg_trigger t ON t.tgname = c.relname || '_if_modified_trg'
                           AND t.tgrelid = c.oid  -- check only respective table
   WHERE  n.nspname = 'public'
   AND    c.relkind = 'r'   -- only regular tables
   AND    t.tgrelid IS NULL -- trigger does not exist yet
LOOP
   EXECUTE format(
      'CREATE TRIGGER %I
       BEFORE INSERT OR UPDATE OR DELETE ON %s
       FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func()'
     , _trg_name, _tbl_oid::text
   );
END LOOP;
END
$do$;

Related answers with more explanation:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228