Danger of idiomatic plpgsql loop for merging data

From: "J(dot) Greg Davidson" <jgd(at)well(dot)com>
To: PostgreSQL General <pgsql-general(at)postgresql(dot)org>
Cc: lynn <lynn(at)creditlink(dot)com>, /Blank Page/ <blankpage2008(at)gmail(dot)com>
Subject: Danger of idiomatic plpgsql loop for merging data
Date: 2010-07-28 21:27:16
Message-ID: 573538171.3904.1280352436483.JavaMail.root@zimbra.well.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Hi fellow PostgreSQL hackers,

I just got burned by the idiomatic loop
documented in the PostgreSQL manual as

Example 39-2. Exceptions with UPDATE/INSERT

I have now replaced this "standard" idiom
with a safer one described below.

What went wrong:

It seems that the table I was either
inserting into or selecting from had
a trigger inserting some associated
data which was sometimes raising a
unique_violation exception, turning the
"standard" idiom into an infinite loop!

My (simplified) old code looked like this:

CREATE TABLE foos (
foo_ foo PRIMARY KEY DEFAULT next_foo();
name_ text UNIQUE NOT NULL;
);

CREATE OR REPLACE
FUNCTION get_foo(text) RETURNS foo AS $$
DECLARE
_foo foo;
BEGIN
LOOP
SELECT foo_ INTO _foo
FROM foos WHERE name_ = $1;
IF FOUND THEN RETURN _foo; END IF;
BEGIN
INSERT INTO foos(name_) VALUES($1);
EXCEPTION
WHEN unique_violation THEN
-- maybe another thread?
END;
END LOOP;
END;
$$ LANGUAGE plpgsql STRICT;

My (simplified) new code is longer but
more flexible, safer and adds logging:

CREATE OR REPLACE
FUNCTION old_foo(text) RETURNS foo AS $$
SELECT foo_ FROM foos WHERE name_ = $1
$$ LANGUAGE SQL STRICT;

CREATE OR REPLACE
FUNCTION new_foo(text) RETURNS foo AS $$
DECLARE
this regprocedure := 'new_foo(text)';
_foo foo;
BEGIN
INSERT INTO foos(name_) VALUES ($1)
RETURNING foo_ INTO _foo;
RETURN _ref;
EXCEPTION
WHEN unique_violation THEN
-- maybe another thread?
RAISE NOTICE '% "%" unique_violation', this, $1;
RETURN NULL;
END;
$$ LANGUAGE plpgsql STRICT;

CREATE OR REPLACE
FUNCTION get_foo(text) RETURNS foo AS $$
SELECT COALESCE(
old_foo($1), new_foo($1), old_foo($1)
)
$$ LANGUAGE sql STRICT;

_Greg

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Ivan Sergio Borgonovo 2010-07-28 21:42:58 Re: Which CMS/Ecommerce/Shopping cart ?
Previous Message Tom Lane 2010-07-28 20:42:26 Re: Need help with full text index configuration