1

For my Hi/Lo implementation, I need a function which will acquire exclusive table lock, update value and select one row. I came up with the following code:

CREATE OR REPLACE FUNCTION bb_next_hi(tbl varchar(35))  
RETURNS setof record AS
$$ 
    LOCK TABLE hilo IN ACCESS EXCLUSIVE MODE;
    OPEN ref FOR SELECT hi as "Hi", lo as "Lo", "table" as "Table" FROM hilo WHERE "table" = $1;
    UPDATE hilo SET hi = hi + 1 WHERE "table" = $1
    RETURN ref;

$$ LANGUAGE plpgsql;

However, when invoking the function, it returns not a row, but one column with a content similar to "unnamed portal 3". I guess I should iterate over returned ref (but how)?

Another approach that I could use is to use UPDATE RETURNING statement, but I'm not sure if race conditions can occur in this case. Any help would be appreciated. Thanks

2
  • Why are you locking the entire table if you just want to lock a single row? Commented Aug 29, 2016 at 17:33
  • I guess there's no other way to lock readers as well. Maybe I'm wrong and misunderstood something, would be greatful for some explanation if so :) Commented Aug 29, 2016 at 21:31

2 Answers 2

2

This seems far more simple to me:

CREATE OR REPLACE FUNCTION bb_next_hi(tbl varchar(35))  
RETURNS setof record AS
$$ 
BEGIN
    LOCK TABLE hilo IN ACCESS EXCLUSIVE MODE;
    RETURN QUERY UPDATE hilo SET hi = hi + 1 WHERE "table" = $1
        RETURNING hi as "Hi", lo as "Lo", "table" as "Table";
END;
$$ LANGUAGE plpgsql;

I also usually define RETURNS TABLE (col1 varchar, col2 varchar...) instead of RETURNS setof record.

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

2 Comments

This returns the updated value of "hi", unlike what the OP was trying to get.
That is true, I didn't think of that. In case of +=1 it should not be much of an issue, though.
1

You are using a cursor variable (which you did not declare, and missing some other PL/pgSQL required elements too) and you return that. Cursor references are indeed called "portals" so that explains your output. A better approach is this:

CREATE OR REPLACE FUNCTION bb_next_hi(tbl varchar(35))  
RETURNS TABLE ("Hi" int, "Lo" int, "Table" varchar(35)) AS $$
BEGIN
    LOCK TABLE hilo IN ACCESS EXCLUSIVE MODE;
    RETURN QUERY SELECT hi, lo, $1 FROM hilo WHERE "table" = $1;
    UPDATE hilo SET hi = hi + 1 WHERE "table" = $1;
    RETURN;
END;
$$ LANGUAGE plpgsql STRICT;

But do you really need an exclusive lock on the table? There are much less invasive approaches to getting a consistent table snapshot.

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.