具有 SELECT 和 INSERT 的函数的 PARALLEL 标签
PARALLEL label for a function with SELECT and INSERT
我所有的数据库活动(PostgreSQL)都是基于我自己的功能。例如,提交验证请求数据,我使用了一个带有一些SELECT
和INSERT
操作的函数。此函数的正确 PARALLEL
标签是什么? SAFE
或 UNSAFE
?
我想我必须使用 SAFE
。我读到如果函数更改数据库或创建新数据库,它必须是 UNSAFE
。但我不会更改数据库!我只是 SELECT
来自 table 和 INSERT
...
CREATE FUNCTION "verification_request_email"(
IN "in_email_address" text,
IN "in_submitted_ip" integer,
OUT "out_submitted_at" integer
) RETURNS integer LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
"uid" integer;
BEGIN
"out_submitted_at":=extract(epoch FROM now() AT TIME ZONE 'utc');
IF EXISTS(SELECT 1 FROM "verification_email" WHERE "submitted_ip"="in_submitted_ip"
AND "submitted_at" > ("out_submitted_at" + 60)) THEN
-- The last email address verification request for this IP address (in_submitted_ip) was
-- less than a minute ago, user must wait for a minute.
RAISE EXCEPTION 'ERR(1)';
END IF;
SELECT "user_id" INTO "uid" FROM "user_email" WHERE "address"="in_email_address" LIMIT 1;
IF("user_id" IS NOT NULL) THEN
IF EXISTS(SELECT 1 FROM "user" WHERE "id"="user_id" AND "status"=B'0' LIMIT 1) THEN
-- User account suspended.
RAISE EXCEPTION 'ERR(2)';
END IF;
END IF;
INSERT INTO "verification_email" VALUES ("in_submitted_ip", "in_submitted_at");
END;
$BODY$;
保留函数的默认值,即 PARALLEL UNSAFE
。写入数据库的函数永远不会是 PARALLEL SAFE
.
Functions and aggregates must be marked PARALLEL UNSAFE
if they
write to the database, access sequences, change the transaction state
even temporarily (e.g., a PL/pgSQL function that establishes an
EXCEPTION
block to catch errors), or make persistent changes to
settings.
大胆强调我的。
相关:
更好的功能
一边做一边考虑这个重写:
CREATE FUNCTION verification_request_email(
in_email_address text,
in_submitted_ip integer,
OUT out_submitted_at integer)
LANGUAGE plpgsql AS -- default PARALLEL UNSAFE
$func$
BEGIN
IF EXISTS ( -- simpler, cheaper
SELECT FROM user_email ue
JOIN user u ON u.id = ue.user_id
WHERE ue.address = in_email_address
AND u.status = B'0') THEN
-- User account suspended.
RAISE EXCEPTION 'ERR(2)';
END IF;
out_submitted_at := extract(epoch FROM now() AT TIME ZONE 'utc');
INSERT INTO verification_email (submitted_ip, submitted_at) -- target column list!
SELECT in_submitted_ip, in_submitted_at
WHERE NOT EXISTS (
SELECT FROM verification_email v
WHERE v.submitted_ip = in_submitted_ip
AND v.submitted_at > (out_submitted_at - 60) -- minus, not plus!
);
IF NOT FOUND THEN
-- The last email address verification request for this IP address (in_submitted_ip) was
-- less than a minute ago, user must wait for a minute.
RAISE EXCEPTION 'ERR(1)';
END IF;
END
$func$;
一个SELECT
和一个INSERT
应该便宜很多
如果您希望像评论中指出的那样有许多并发调用,则这一点尤其重要。
我所有的数据库活动(PostgreSQL)都是基于我自己的功能。例如,提交验证请求数据,我使用了一个带有一些SELECT
和INSERT
操作的函数。此函数的正确 PARALLEL
标签是什么? SAFE
或 UNSAFE
?
我想我必须使用 SAFE
。我读到如果函数更改数据库或创建新数据库,它必须是 UNSAFE
。但我不会更改数据库!我只是 SELECT
来自 table 和 INSERT
...
CREATE FUNCTION "verification_request_email"(
IN "in_email_address" text,
IN "in_submitted_ip" integer,
OUT "out_submitted_at" integer
) RETURNS integer LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
"uid" integer;
BEGIN
"out_submitted_at":=extract(epoch FROM now() AT TIME ZONE 'utc');
IF EXISTS(SELECT 1 FROM "verification_email" WHERE "submitted_ip"="in_submitted_ip"
AND "submitted_at" > ("out_submitted_at" + 60)) THEN
-- The last email address verification request for this IP address (in_submitted_ip) was
-- less than a minute ago, user must wait for a minute.
RAISE EXCEPTION 'ERR(1)';
END IF;
SELECT "user_id" INTO "uid" FROM "user_email" WHERE "address"="in_email_address" LIMIT 1;
IF("user_id" IS NOT NULL) THEN
IF EXISTS(SELECT 1 FROM "user" WHERE "id"="user_id" AND "status"=B'0' LIMIT 1) THEN
-- User account suspended.
RAISE EXCEPTION 'ERR(2)';
END IF;
END IF;
INSERT INTO "verification_email" VALUES ("in_submitted_ip", "in_submitted_at");
END;
$BODY$;
保留函数的默认值,即 PARALLEL UNSAFE
。写入数据库的函数永远不会是 PARALLEL SAFE
.
Functions and aggregates must be marked
PARALLEL UNSAFE
if they write to the database, access sequences, change the transaction state even temporarily (e.g., a PL/pgSQL function that establishes anEXCEPTION
block to catch errors), or make persistent changes to settings.
大胆强调我的。
相关:
更好的功能
一边做一边考虑这个重写:
CREATE FUNCTION verification_request_email(
in_email_address text,
in_submitted_ip integer,
OUT out_submitted_at integer)
LANGUAGE plpgsql AS -- default PARALLEL UNSAFE
$func$
BEGIN
IF EXISTS ( -- simpler, cheaper
SELECT FROM user_email ue
JOIN user u ON u.id = ue.user_id
WHERE ue.address = in_email_address
AND u.status = B'0') THEN
-- User account suspended.
RAISE EXCEPTION 'ERR(2)';
END IF;
out_submitted_at := extract(epoch FROM now() AT TIME ZONE 'utc');
INSERT INTO verification_email (submitted_ip, submitted_at) -- target column list!
SELECT in_submitted_ip, in_submitted_at
WHERE NOT EXISTS (
SELECT FROM verification_email v
WHERE v.submitted_ip = in_submitted_ip
AND v.submitted_at > (out_submitted_at - 60) -- minus, not plus!
);
IF NOT FOUND THEN
-- The last email address verification request for this IP address (in_submitted_ip) was
-- less than a minute ago, user must wait for a minute.
RAISE EXCEPTION 'ERR(1)';
END IF;
END
$func$;
一个SELECT
和一个INSERT
应该便宜很多
如果您希望像评论中指出的那样有许多并发调用,则这一点尤其重要。