Sam*_*eau 8 sql postgresql plpgsql
我有使用UUID的表.我希望能够在有或没有UUID的情况下插入新行,因为有时客户端会生成UUID,有时则不会.
每个表都有它的核心:
CREATE TABLE IF NOT EXISTS person (
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用函数来插入行.我希望能够传递一个NULL id并获得一个默认值(生成的UUID).我有这样的事情:
CREATE OR REPLACE FUNCTION create_person(
id UUID
) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
INSERT INTO person( id )
VALUES (
COALESCE(id,default)
);
RETURN FOUND;
END;
$$;
Run Code Online (Sandbox Code Playgroud)
我试过这个:
INSERT INTO person ( id ) VALUES (
COALESCE(id, default),
);
Run Code Online (Sandbox Code Playgroud)
还有这个:
INSERT INTO person ( id ) VALUES (
CASE WHEN id IS NULL THEN default ELSE id END
);
Run Code Online (Sandbox Code Playgroud)
这有效,但它重复gen_random_uuid()代码:
INSERT INTO person ( id ) VALUES (
COALESCE(id, gen_random_uuid()),
);
Run Code Online (Sandbox Code Playgroud)
同样这也有效,但也有同样的问题:
INSERT INTO person ( id ) VALUES (
CASE WHEN id IS NULL THEN gen_random_uuid() ELSE id END
);
Run Code Online (Sandbox Code Playgroud)
有没有办法在不重复gen_random_uuid()代码的情况下执行此操作?
这会更好plpgsql吗?
无法重新使用列上定义的默认值.默认值仅用于定义INSERT在未指定值时发生的情况.通过此定义,null值仍然是"指定的",因此不能使用默认值.
您对某人可能不使用该功能的评论表明,触发器比简单功能更符合您的要求.
https://www.postgresql.org/docs/current/static/plpgsql-trigger.html
CREATE OR REPLACE FUNCTION default_id() RETURNS TRIGGER AS $default_id$
BEGIN
IF (NEW.id IS NULL) THEN
NEW.id := gen_random_uuid();
END IF;
RETURN NEW;
END;
$default_id$ LANGUAGE plpgsql;
CREATE TRIGGER default_id_trigger
BEFORE INSERT OR UPDATE ON person
FOR EACH ROW EXECUTE PROCEDURE default_id();
Run Code Online (Sandbox Code Playgroud)
如果你想用函数做这个,那么最简单的方法就是在插入之前分配值:
CREATE OR REPLACE FUNCTION create_person(
id UUID
) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
IF id IS NULL THEN
id := gen_random_uuid();
END IF;
-- OR
-- id := coalesce(id, gen_random_uuid());
INSERT INTO person( id )
VALUES (id);
RETURN FOUND;
END;
$$;
Run Code Online (Sandbox Code Playgroud)
核心问题是关键字的特殊性质DEFAULT在一个VALUES连接到表达INSERT.每个文件:
在
VALUES出现在a 的顶层的列表中INSERT,表达式可以替换DEFAULT为表示应插入目标列的默认值.在其他情境中出现DEFAULT时无法使用VALUES.
大胆强调我的.具体来说,DEFAULT不能是函数的参数:
COALESCE(function_parameter, DEFAULT) -- not possibleRun Code Online (Sandbox Code Playgroud)
根据具体要求,有多种方式.
这个函数不需要知道实际的默认值person.id- 这似乎就是你所追求的:
CREATE OR REPLACE FUNCTION create_person(_id UUID)
RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS
$func$
BEGIN
IF _id IS NULL THEN -- no UUID provided
INSERT INTO myschema.person(id) -- see below about schema name
VALUES (DEFAULT); -- use the key word DEFAULT
ELSE -- UUID provided
INSERT INTO myschema.person(id)
VALUES (_id);
END IF;
RETURN FOUND; -- (return value pointless so far)
END
$func$;
Run Code Online (Sandbox Code Playgroud)
避免对参数和涉及的表列使用相同的名称.由于函数体中的每个SQL命令中都可以看到函数参数,因此可能会导致命名冲突非常混乱(即使INSERT现代Postgres中的目标列不受此限制).我_id用作参数名称.
未在其中提及的其他列的默认值将自动INSERT填写.我使用关键词是因为我们需要列出至少一个目标列.DEFAULTINSERT
该boolean返回值是在这个演示毫无意义的,因为它总是 true(除非你有可能会跳过行触发器).
相关答案以及可能的替代方案和大量解释:
除此之外:您应该对函数中的所有函数和表名进行模式限定SECURITY DEFINER- 或者(如果您不确定,可能更好)明确设置search_path以防止可能的攻击.更多:
| 归档时间: |
|
| 查看次数: |
4872 次 |
| 最近记录: |