I have an interesting problem that me and my collegue troubles for some time now.
I have a PL/pgSQL function in a PostgreSQL-8.3 (sorry for that old version, I can't change that) that does the following four things:
- Get a new serial (ID) from a sequence
- Insert a couple of records into a table with that serial
- Send a notify signal that an insertion took place
- Return the ID to the caller.
Simplified function:
CREATE OR REPLACE FUNCTION add_entry(_user_name text, _visible_attr integer[])
RETURNS bigint AS
$BODY$
DECLARE
user_name text := '#' || $1;
user_id bigint;
BEGIN
-- get the ID from the sequence
SELECT nextval('my_sequence') INTO user_id;
-- insert the name (and some other data not shown here) 5x
FOR item IN 1..5
LOOP
INSERT INTO mytable
(id,index,username,visible)
VALUES (user_id,item,user_name,$2[item]);
END LOOP;
-- send notify that an insertion took place
notify my_notify;
RETURN user_id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
So, my collegue called this function from his application. He gets the returned ID and uses another thread (DB pooling) in his application to call a function which shall return the data previously inserted with that ID. However, this doesn't work the first time. Only with the second request he is able to select the data. It seems as if that INSERT isn't finished while the function already returns?!
We checked mutiple times, the data will be inserted into the table correctly but somehow it is not available as fast as the return value (the ID from the sequence) is available! Why is that so?
Update: wrong assumption
I examined further and reduced the example to a simple query which really shows the problem:
select * from mytable where id = (select add_entry('MyTestUser'));
This query returns no rows. But if I do that in two seperate steps I can select the data which I inserted with the add_entry function.
I have no clue what I'm doing wrong or how I could speed up the insertion...
From the 8.3 manual
Since the update is done in the
select
itself the inserted row will not be seen.http://www.postgresql.org/docs/8.3/static/tutorial-transactions.html
Change the function to return
setof mytable
. It can be plain SQL. To change the return type the function must be dropped firstThe notification must happen before anything is returned from the function. It is not a problem as if the insert fails the transaction rolls back including the notification. Call it like
The select will not see the modified table but the returned
mytable
rows.If it is necessary for the function to be
plpgsql
then usereturn query