35

How to prevent recursive execution of trigger? Let's say I want to construct a "tree-able" description on chart of account. So what I do is when a new record is inserted/updated, I update the the parent record's down_qty, so this would trigger the update trigger recursively.

Right now, my code is ok - I put this on UPDATE trigger's first line:

-- prevents recursive trigger
if new.track_recursive_trigger <> old.track_recursive_trigger then
    return new;
end if;

And this is the sample code from my trigger when I need to update the parent record's qty:

update account_category set 
    track_recursive_trigger = track_recursive_trigger + 1, -- i put this line to prevent recursive trigger
    down_qty = down_qty - (old.down_qty + 1)
where account_category_id = m_parent_account;

I'm thinking if there's a way in PostgreSQL to detect recursive trigger without introducing a new field, something analogous to MSSQL's trigger_nestlevel.

[EDIT]

I loop inside the tree, I need to bubble up the down_qty of each account_category back to its root. For example, I insert a new account category, it needs to increment the down_qty of its parent account_category, likewise when I change the account category's parent account_category, I need to decrement the down_qty of account_category's previous parent account_category. Though I think it can, I'm not letting PostgreSQL do the recursive trigger. I used MSSQL before where the trigger recursive depth level is limited only up to 16 levels.

andr
  • 15,970
  • 10
  • 45
  • 59
Hao
  • 8,047
  • 18
  • 63
  • 92
  • 1
    I think you should explain in a bit more details, because as far as I can see there is no danger of recursive call in here unless you have either: record which is its own parent, or loops in your tree (which is not tree then). –  Apr 02 '09 at 16:43

4 Answers4

123

This is what I do in PostgreSQL 9.2, although I must admit I did not find this approach documented. There is a function pg_trigger_depth() documented here, which I use to differentiate between original and nested calls in the trigger.

CREATE TRIGGER trg_taxonomic_positions
AFTER INSERT OR UPDATE OF taxonomic_position
ON taxon_concepts
FOR EACH ROW
WHEN (pg_trigger_depth() = 0)
EXECUTE PROCEDURE trg_taxonomic_positions()
andr
  • 15,970
  • 10
  • 45
  • 59
agnessa
  • 1,376
  • 1
  • 8
  • 5
  • 6
    Brilliant! Unfortunately, this function is not present in 9.1 :(. I wonder if there is a replacement for it... – mvp May 22 '13 at 06:48
  • 4
    Lifesaver. Confirmed it works on 9.4 like a charm. Someone should help this make its way to the documentation. As far as I've looked, haven't seen any meaningful examples on the trigger `WHEN (condition)` clause. This definitely qualifies. – Thalis K. May 21 '15 at 12:15
  • 11
    It work, but pay some attention because that function is not to measure recursive. If other trigger originate the actual trigger, that function will produce 1 instead 0. – Natan Medeiros Feb 05 '18 at 17:22
9

In pg, it's up to you to track trigger recursion.

If a trigger function executes SQL commands then these commands might fire triggers again. This is known as cascading triggers. There is no direct limitation on the number of cascade levels. It is possible for cascades to cause a recursive invocation of the same trigger; for example, an INSERT trigger might execute a command that inserts an additional row into the same table, causing the INSERT trigger to be fired again. It is the trigger programmer's responsibility to avoid infinite recursion in such scenarios.

https://www.postgresql.org/docs/13/trigger-definition.html

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
tpdi
  • 34,554
  • 11
  • 80
  • 120
  • Sorry but have to downvote as the question asked **how** to prevent recursion. This answer states the user needs to track trigger recursion, which the question already acknowledges, and is what is being asked how to solve. – phemmer Jun 11 '20 at 00:43
2

To avoid unbounded recursion, see my answer here. As others have commented, if your data structure is a true tree (the root(s) will have no parent(s)) and the recursion will always stop at the root(s). For nodes with only one parent pointer, the only way for unbounded recursion would be if there were loops present. (the method in my link will visit any node at most once)

Community
  • 1
  • 1
wildplasser
  • 43,142
  • 8
  • 66
  • 109
2

At the beggining of the definition of the trigger you can disable triggers on that particular table, and reenable them at the end (and make sure an exception doesn't terminate the execution before expected!). This has many deep holes, but may work for some light implementations. Notice that for this implementation, you will also need priviliges to disable triggers.

diegom_ch
  • 29
  • 1
  • 2
    this doesn't work, because: cannot ALTER TABLE "trigger_table" because it is being used by active queries in this session – Doctor Eval Sep 26 '17 at 13:47