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;
$$;