Update: After extensive instrumenting of my code, I found a place where another developer inserts into this table without using the 'upsert' function. However, due to how the methods were grouped and the exception captured, the stack trace suggested that this function was in error, when, in fact, not calling the function was the error. The code below is fine (with the caveat added by Daniel).
I have the following plpgsql function:
-- http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/
CREATE OR REPLACE FUNCTION upsert_person_site(
curr_site_id INTEGER,
curr_person_id INTEGER,
curr_job_title CHARACTER VARYING(128)
) RETURNS void as $$
BEGIN
-- strictly speaking, running the update first is not needed and
-- duplicate code, but exceptions are relatively expensive.
-- Also, note that we refuse to update with a NULL job title because an
-- import may simply fail to specify one.
UPDATE person_site
SET job_title = curr_job_title
WHERE site_id = curr_site_id
AND person_id = curr_person_id
AND curr_job_title IS NOT NULL;
IF FOUND THEN
RETURN;
END IF;
BEGIN
INSERT INTO person_site ( site_id, person_id, job_title )
VALUES ( curr_site_id, curr_person_id, curr_job_title );
EXCEPTION WHEN OTHERS THEN
UPDATE person_site
SET job_title = curr_job_title
WHERE site_id = curr_site_id
AND person_id = curr_person_id
AND curr_job_title IS NOT NULL;
END;
RETURN;
END;
$$ language plpgsql;
The intent is to do an update if the record exists, or an insert if it does not. The intended logic is:
// duplicating the update to avoid trapping an expensive exception
try to update the record
if successful
return
try
// I believe it's failing here
insert a new record
catch
update an existing record
The person_site
table has a person_site_pkey
on the person_id
and site_id
fields. However, many times when this function is run, I get an exception stating duplicate key value violates unique constraint "person_site_pkey" at ...
.
Can someone help me understand what I'm missing? I thought the EXCEPTION WHEN OTHERS
block would trap that.
This is Postgresql 8.4.13 running on Debian Squeezebox. The application code is Perl and uses the DBD::Pg module.