稳定函数调用易失性函数:并发问题

发布于 2024-12-03 19:44:29 字数 1863 浏览 0 评论 0原文

该数据库包含一个用户表。每当用户的用户名是发送到数据库的数据的一部分(即:通过函数)时,用户就会被添加到表中,并且用户名在表中应该是唯一的。单个用户名也可能在任何给定的函数调用中出现多次。

对于每个用户名,我想获取其现有的 ID 或将其插入用户表中并返回 ID。

我提出的解决方案是一个 STABLE 函数,它首先尝试从用户表中进行选择,如果失败,它会调用一个 VOLATILE 辅助函数来尝试插入到用户表中。我更喜欢 STABLE,因为该函数的结果对于事务的其余部分是相同的,所以我希望它在用户名被多次包含的情况下进行优化,或者它被传递给其他函数寻找它的 ID。

我的问题是:我的初始函数中的 STABLE 是否意味着初始函数永远不会看到并发插入(导致辅助函数中出现异常),从而导致无限循环?

我已包含以下定义。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

简单爱 2024-12-10 19:44:30

易失性或稳定标志与并发性无关。这些标志的主要用途是用于查询优化。您必须从事务隔离级别中选择一个并使用适当的解决方案 - 取决于您是否使用 REPEATABLE READ 或 READ COMMITED 级别。

实际上函数 getUserID 应该被标记为 易失性的,因为它调用了另一个易失性函数。如果您使用 REPEATABLE READ 级别,则可能会出现无限循环。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文