0

The database contains a table of users. A user is added to the table whenever their username is part of the data being sent to the database (i.e.: through a function), and a username should be unique in the table. A single username may also appear multiple times in any given function call.

For each username, I would like to get its existing ID or insert it into the users table and return the ID.

The solution I've come up with is a STABLE function that first tries to select from the users table, and if it fails it calls a VOLATILE helper function that tries to insert into the user's table. I prefer STABLE because the result of the function will be the same for the rest of the transaction, so I would like it optimized away in the case where the username was included multiple times, or where it gets passed on to other functions that are also looking for its ID.

My question is: Will the STABLE from my initial function mean that a concurrent insert (causing an exception in the helper function) will never been seen by the initial function, and thus cause an infinite loop?

I've included the definitions below.

CREATE SCHEMA orgnztn;
CREATE TABLE orgnztn.tUsers (
    id serial NOT NULL,
    usrid text NOT NULL,
    PRIMARY KEY (id),
    UNIQUE (usrid)
);

CREATE OR REPLACE FUNCTION orgnztn.getUserID (
    IN  p_usrid             text
)
    RETURNS integer
    LANGUAGE plpgsql
    STABLE
    CALLED ON NULL INPUT
    SECURITY INVOKER
    AS $$
        DECLARE
            p_id integer;
        BEGIN
            IF p_usrid IS NULL THEN
                RETURN NULL;
            END IF;
            p_usrid = upper(p_usrid);
            LOOP
                SELECT id INTO p_id
                    FROM orgnztn.tUsers
                    WHERE usrid = p_usrid
                    FETCH FIRST 1 ROWS ONLY;
                IF found THEN
                    RETURN p_id;
                END IF;
                BEGIN
                    RETURN orgnztn.getUserID_helper(p_usrid);
                    EXCEPTION WHEN unique_violation THEN
                        -- loop
                END;
            END LOOP;
        END;
    $$;
CREATE OR REPLACE FUNCTION orgnztn.getUserID_helper (
    IN  p_usrid             text
)
    RETURNS integer
    LANGUAGE plpgsql
    VOLATILE
    CALLED ON NULL INPUT
    SECURITY INVOKER
    AS $$
        DECLARE
            p_id integer;
        BEGIN
            INSERT INTO orgnztn.tUsers (usrid)
                VALUES (p_usrid)
                RETURNING id INTO p_id;
            RETURN p_id;
        END;
    $$;

1 Answer 1

1

volatile or stable flags are not related to the concurrency. Main usage of these flags are for query optimization. You have to select a one from transaction isolation level and use a adequate solution - depends if you use REPEATABLE READ or READ COMMITED level.

Actually function getUserID should be marked as volatile, because it call a other volatile function. If you use a REPEATABLE READ level, then there can be a infinite loop.

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

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.