Re: functions, transactions, key violations - Mailing list pgsql-general

From Michael Glaesemann
Subject Re: functions, transactions, key violations
Date
Msg-id 46C6F2D4-CA9D-43DF-B59D-00F8E7BAB1E7@seespotcode.net
Whole thread Raw
In response to Re: functions, transactions, key violations  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: functions, transactions, key violations
List pgsql-general
On Jun 4, 2008, at 5:39 PM, Tom Lane wrote:

> I think you've anonymized the example into nonsense :-(.

Now that I've provided yet another example of the perils of not
providing the exact code, I've tried to patch it (below, for those of
you willing to give me a second chance).

However, my point regarding the example in the docs still holds. Why
is the exception block necessary? Doesn't wrapping the statements in a
function ensure the unique_violation couldn't occur?
Again, this is from
<http://www.postgresql.org/docs/8.3/interactive/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE
 >

CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION merge_db(key INT, data TEXT)
RETURNS VOID AS $$
BEGIN
   LOOP -- first try to update the key
     UPDATE db SET b = data WHERE a = key;
     IF found THEN
        RETURN;
     END IF;
   -- not there, so try to insert the key
   -- if someone else inserts the same key concurrently,
   -- we could get a unique-key failure
     BEGIN
       INSERT INTO db(a,b) VALUES (key, data);
       RETURN;
     EXCEPTION WHEN unique_violation THEN
       -- do nothing, and loop to try the UPDATE again
     END;
   END LOOP;
END
$$ LANGUAGE plpgsql;
Michael Glaesemann
grzm seespotcode net


CREATE TABLE purchases (
  purchase_id SERIAL PRIMARY KEY,
  item_id INT NOT NULL,
  purchased_by int NOT NULL,
  purchase_price INT NOT NULL,
  purchased_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT
CURRENT_TIMESTAMP,
  purchase_status INT NOT NULL DEFAULT 1
);

CREATE UNIQUE INDEX
purchases_purchase_id_where_purchase_status_eq_1_key on purchases
(item_id) WHERE purchase_status = 1;

CREATE OR REPLACE FUNCTION
purchase(IN in_item_id integer,
          IN in_purchased_by bigint,
          IN in_purchase_price integer)
RETURNS VOID AS
$BODY$
BEGIN
-- some selects
UPDATE purchases
   SET purchase_status = 0
   WHERE item_id = in_item_id
         AND purchase_status = 1;
INSERT INTO purchases (item_id, purchased_by, purchase_price)
    VALUES (in_item_id, in_purchased_by, in_purchase_price);
-- some more manipulation
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE;


pgsql-general by date:

Previous
From: Tom Lane
Date:
Subject: Re: functions, transactions, key violations
Next
From: Stephan Szabo
Date:
Subject: Re: Script errors on run