从PostgreSQL函数返回NULL而不是复合值



让我们在PostgreSQL中拥有以下东西:

CREATE TYPE struct AS (x INT, y INT);
CREATE TABLE tbl (a INT, b struct);
CREATE FUNCTION find_tbl_entry(clear BOOL) RETURNS tbl AS $$
DECLARE k tbl;
BEGIN
IF clear THEN
k.b := NULL;
END IF;
RETURN k;
END;
$$ LANGUAGE plpgsql;

也就是说,我们有一个函数返回复合类型的值tbl,而复合类型struct的属性b又作为其属性之一。(原始问题更有趣 - 一个翻译函数返回一行,并相应地翻译了一些属性;不过,问题归结为呈现的代码。

SELECT find_tbl_entry(FALSE)导致(,(,)),即 NULL 作为a的值和一个空结构(NULL 和 NULL 的一对)作为b的值,这在某种程度上是意料之中的。

现在,即使SELECT find_tbl_entry(TRUE)结果是(,(,)),即即使b属性显式设置为NULL,结果也不是NULL,但它仍然是空结构。

如何让函数find_tbl_entryb属性中返回NULL


编辑:事实证明,奇怪的是分配k.b := NULL。扩展函数时:

k.b := NULL;
RAISE NOTICE '%', k.b IS DISTINCT FROM NULL;

它发出"注意:t"。因此,似乎将NULL分配给复合值实际上分配了具有所有属性NULL的复合。考虑到NULL值与存储在表中时(NULL,NULL)值是可区分的,这很奇怪(UPDATE tbl SET b = NULL导致每一行b IS NOT DISTINCT FROM NULL保持;另一方面,UPDATE tbl SET b = (NULL,NULL)对于该测试false)。

有必要:

直接返回null,而不是返回分配的变量:

create or replace function find_tbl_entry()
returns tbl as $$
declare s struct;
begin
s := null;
return (1, nullif(s, (null,null)::struct));
end;
$$ language plpgsql;
select a, b, b is null from find_tbl_entry();
a | b | ?column? 
---+---+----------
1 |   | t

或者在函数使用时进行比较:

select coalesce(nullif((find_tbl_entry()).b, (null,null)::struct), (1,2)::struct);
coalesce 
----------
(1,2)

在SQL中,当复合值的所有组件都NULL时,复合值(SQL标准称之为"度>值1")是NULL的,所以PostgreSQL的行为是正确的。

ISO/IEC 9075-2:2003,第8章,第7节,说:

8.7<空谓词>

功能

为空值指定测试。

格式

<null predicate> ::= <row value predicand> <null predicate part 2>
<null predicate part 2> ::= IS [ NOT ] NULL

语法规则

没有。

访问规则

没有。

一般规则

1) 设R为<行值谓词>的值。

2)案例:

    一)如果R是空值,则 "RIS NULL" 为True

    b)否则:

        一)"RIS NULL"的值是

            箱:

            1)如果R中每个字段的值都是空值,则为 True

            2)否则,为假

        二)"RIS NOT NULL"的值为

            箱:

            1)如果R中没有字段的值为空值,则为 True

            2)否则,为假

(如果你认为这很疯狂,你并不孤单。

您可以验证 PostgreSQL 是否正确处理了这样的值:

SELECT (ROW(NULL,ROW(NULL,NULL))::tbl).b IS NULL;
?column?
----------
t
(1 row)

我知道你宁愿希望值读取(NULL, NULL),但你不能用PostgreSQL得到它。我希望它仍然会正确运行,这对您来说是一种安慰。

最新更新