我有使用UUID的表。我希望能够在有或没有UUID的情况下插入新行,因为有时客户端会生成UUID,否则它将不会生成UUID。
每个表都有它的核心:
<p:dashboard id="sortDashboard" model="#{customAttributesSettings.sortModel}" disabled="#{!customAttributesSettings.editable}">
<p:ajax event="reorder" listener="#{customAttributesSettings.changedSort}" update="@form:customTabSet:sortCurrent" immediate="true"/>
<c:forEach var="sortColumn" items="#{customAttributesSettings.availableSortColumns}">
<p:panel id="#{sortColumn.id}" header="#{sortColumn.name}">
<!--some components-->
</p:panel>
</c:forEach>
</p:dashboard>
我正在尝试使用函数插入行。我希望能够传递一个NULL id并获得一个默认值(生成的UUID)。我有这样的事情:
CREATE TABLE IF NOT EXISTS person (
id UUID PRIMARY KEY DEFAULT gen_random_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;
$$;
和此:
INSERT INTO person ( id ) VALUES (
COALESCE(id, default),
);
这样可行,但它会重复gen_random_uuid()代码:
INSERT INTO person ( id ) VALUES (
CASE WHEN id IS NULL THEN default ELSE id END
);
同样这也有效,但也存在同样的问题:
INSERT INTO person ( id ) VALUES (
COALESCE(id, gen_random_uuid()),
);
有没有办法在不重复INSERT INTO person ( id ) VALUES (
CASE WHEN id IS NULL THEN gen_random_uuid() ELSE id END
);
代码的情况下执行此操作?
使用gen_random_uuid()
更好吗?
答案 0 :(得分:6)
无法在列上重复使用已定义的默认值。默认值仅用于定义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();
如果你想用函数做这个,那么最简单的方法就是在插入之前分配值:
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;
$$;
答案 1 :(得分:6)
核心问题是附加到DEFAULT
的{{1}}表达式中关键字VALUES
的特殊性。 Per documentation:
在显示在
INSERT
顶层的VALUES
列表中, 表达式可以由INSERT
替换,以指示目标 应插入列的默认值。DEFAULT
时无法使用DEFAULT
出现在其他情境中。
大胆强调我的。具体来说,VALUES
不能成为函数的参数:
DEFAULT
有多种方法,具体取决于完全要求。
这个函数不需要知道
的实际默认值 - 这似乎是你所追求的:COALESCE(function_parameter, DEFAULT) -- not possible
person.id
避免对参数和涉及的表列使用相同的名称。由于函数体中的每个SQL命令中都可以看到函数参数,因此可能会导致非常混乱的命名冲突(即使现代Postgres中的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$;
的目标列不受此限制)。我使用INSERT
作为参数名称。
_id
中未提及的 其他列 的默认值已自动填入 。我使用关键字INSERT
,因为我们需要列出DEFAULT
的至少一个目标列。
此演示中的INSERT
返回值毫无意义,因为总是 boolean
(除非您有可能跳过该行的触发器)。
相关答案以及可能的替代方案和大量解释:
除此之外:您应该在true
函数 - 或 <中对 所有 函数和表名进行架构限定/ strong>(如果你不确定,可能会更好)明确设置SECURITY DEFINER
以防御可能的攻击。更多:
答案 2 :(得分:0)
重载gen_random_uuid
功能:
create or replace function gen_random_uuid()
returns uuid as $$
select 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid;
$$ language sql;
create or replace function gen_random_uuid(_id uuid)
returns uuid as $$
select coalesce(_id, gen_random_uuid());
$$ language sql;
像往常一样创建表格:
create table if not exists person (
id uuid default gen_random_uuid()
);
调用gen_random_uuid
函数中的create_person
函数:
create or replace function create_person(_id uuid)
returns boolean as $$
insert into person (id) values (gen_random_uuid(_id))
returning true;
$$ language sql;
然后所有插入变体都有效:
insert into person (id) values (default);
select create_person(null);
select create_person('b1eebc99-9c0b-4ef8-bb6d-6bb9bd380a22'::uuid);