0

I have a trigger that calls a function. It basically must update the same table after update. But it get stacks and does not update anything.

This is my trigger:

    CREATE OR REPLACE FUNCTION invtransferences_products_after() 
    RETURNS TRIGGER AS 
$BODY$
DECLARE
    TR invtransferences_products%ROWTYPE;
    v_transfer_cost NUMERIC;
BEGIN
    IF(TG_OP='INSERT') THEN
        TR := NEW;
        RAISE NOTICE 'INVTRANSFERENCE PRODUCT ADDED %',TR.id;
        UPDATE invtransferences_products  
        SET product_cost = (get_product_composition_cost(product_id, 0)*quantity )
        WHERE invtransferences_products.id=TR.id;
    ELSE
        IF (TG_OP='UPDATE') THEN
            TR := NEW;
            RAISE NOTICE 'INVTRANSFERENCE PRODUCTS UPDATED %',TR.id;
            UPDATE invtransferences_products  
            SET product_cost = (get_product_composition_cost(product_id, 0)*quantity )
            WHERE invtransferences_products.id=TR.id;
        END IF;
    END IF;
    RETURN TR;
END
$BODY$
LANGUAGE plpgsql;

This is my table invtransferences_products:

CREATE TABLE invtransferences_products
(
   id                  serial      NOT NULL,
   invtransference_id  bigint       NOT NULL,
   product_id          bigint       NOT NULL,
   quantity            numeric      DEFAULT 1 NOT NULL,
   created             timestamp    DEFAULT now() NOT NULL,
   modified            timestamp,
   rcv_quantity        numeric      DEFAULT 0 NOT NULL,
   pnd_quantity        numeric      DEFAULT 0 NOT NULL,
   product_cost        numeric
);

ALTER TABLE invtransferences_products
   ADD CONSTRAINT invtransferences_products_pkey
   PRIMARY KEY (id);

ALTER TABLE invtransferences_products
  ADD CONSTRAINT invtransferences_products_invtransference_id_fkey FOREIGN KEY (invtransference_id)
  REFERENCES invtransferences (id)
  ON UPDATE CASCADE
  ON DELETE CASCADE;

COMMIT;

What's wrong?? Help please.

0

2 Answers 2

2

The problem is that the UPDATE statement in the trigger function causes the trigger to fire again.

Instead of issuing a separate update, you should manipulate the data in NEW.

Something like:

CREATE OR REPLACE FUNCTION invtransferences_products_after() 
    RETURNS TRIGGER AS 
$BODY$
BEGIN
    IF(TG_OP='INSERT') THEN
        RAISE NOTICE 'INVTRANSFERENCE PRODUCT ADDED %',NEW.id;
    ELSE
        IF (TG_OP='UPDATE') THEN
            RAISE NOTICE 'INVTRANSFERENCE PRODUCTS UPDATED %',NEW.id;
        END IF;
    END IF;

    NEW.product_cost := get_product_composition_cost(NEW.product_id,0)*NEW.quantity ;
    RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;

Fiddle at: SQLFiddle

Sign up to request clarification or add additional context in comments.

Comments

1

This trigger will cause infinite recursion ending up with a stack depth limit exceeded error, because it issues another UPDATE of the table each time an INSERT/UPDATE occurs on the same table.

The solution is, instead of this:

 UPDATE invtransferences_products  
        SET product_cost = (get_product_composition_cost(product_id, 0)*quantity )
        WHERE invtransferences_products.id=TR.id;

It should do that:

  NEW.product_cost := get_product_composition_cost(NEW.product_id, 0)*NEW.quantity;

and declare the trigger as running BEFORE UPDATE or INSERT (not AFTER).

That's the more logical approach.

As a workaround, recursion can also be blocked outside of the trigger. This is answered in Prevent recursive trigger in PostgreSQL.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.