如何确保存储的函数始终返回TRUE或FALSE?

时间:2016-03-02 09:15:42

标签: postgresql stored-procedures plpgsql coalesce postgresql-9.5

使用以下存储的函数,我想验证用户数据:

CREATE OR REPLACE FUNCTION check_user(
        in_social integer,
        in_sid varchar(255),
        in_auth varchar(32))
        RETURNS boolean AS
$func$
        SELECT MD5('secret word' || in_social || in_sid) = in_auth;
$func$ LANGUAGE sql IMMUTABLE;

我将在循环访问另一个存储函数中的JSON对象数组时调用它 - 如果它为任何JSON对象返回RAISE EXCEPTION,它将FALSE(从而回滚整个事务)。

我没有在这里转储我的第二个存储函数的源代码,而是在下面准备了3个简单的测试函数 -

CREATE OR REPLACE FUNCTION test1() RETURNS void AS
$func$
BEGIN
        IF NOT check_user(42, 'user1', '56db1046fa7b664c9b3d05bf7413552a') THEN
                RAISE NOTICE 'invalid user';
        ELSE
                RAISE NOTICE 'valid user';
        END IF;
END
$func$ LANGUAGE plpgsql;

第一个功能按预期工作并打印valid user

CREATE OR REPLACE FUNCTION test2() RETURNS void AS
$func$
BEGIN
        IF NOT check_user(42, 'user2', '56db1046fa7b664c9b3d05bf7413552a') THEN
                RAISE NOTICE 'invalid user';
        ELSE
                RAISE NOTICE 'valid user';
        END IF;
END
$func$ LANGUAGE plpgsql;

第二个功能按预期工作并打印invalid user

CREATE OR REPLACE FUNCTION test3() RETURNS void AS
$func$
BEGIN
        IF NOT check_user(42, 'user1', NULL) THEN
                RAISE NOTICE 'invalid user';
        ELSE
                RAISE NOTICE 'valid user';
        END IF;
END
$func$ LANGUAGE plpgsql;

第3个功能无法按预期工作并打印valid user

这是因为check_user()返回NULL而不是布尔值。

COALESCE可以围绕check_user()语句中的IF调用进行处理......但是有没有更好的方法可以解决这个问题?

3 个答案:

答案 0 :(得分:1)

我会在check_user()函数中添加coalesce,因此它总是返回true或false。

    SELECT MD5('secret word' || in_social || in_sid) = coalesce(in_auth,'');

如果其他值也可能为NULL,甚至是下面的选项:

    SELECT MD5('secret word' || coalesce(in_social,-1)::varchar || coalesce(in_sid,'')) = coalesce(in_auth,'');

在那里" -1"永远不会分配给in_social的值

答案 1 :(得分:1)

检查is [not] distinct from

select md5('secret word' || in_social || in_sid) is not distinct from in_auth;

但请注意,如果双方评估为null,则比较将返回true

  

对于非空输入,IS DISTINCT FROM与<>相同运营商。但是,如果两个输入都为null,则返回false,如果只有一个输入为null,则返回true。类似地,对于非空输入,IS NOT DISTINCT FROM与=相同,但是当两个输入都为空时返回true,而当只有一个输入为null时返回false。因此,这些结构有效地表现为null是正常数据值,而不是“未知”。

更简单的是只声明函数strict并将返回值与is not true进行比较:

if check_user(42, 'user1', null) is not true then raise notice 'invalid user';
  

STRICT表示只要其任何参数为null,该函数始终返回null。如果指定了此参数,则在存在null参数时不执行该函数;而是自动假设空结果。

这具有避免执行该功能的任何成本的额外好处。

答案 2 :(得分:0)

以下是我可能会使用的内容,因为该功能与安全相关,因此我不想记住任何边缘情况(例如在将IF check_user(...) IS NOT TRUE声明为{{1}时强制使用STRICT }})在将来使用它时 -

CREATE OR REPLACE FUNCTION check_user(
        in_social integer, 
        in_sid varchar(255), 
        in_auth varchar(32))
        RETURNS boolean AS
$func$
        SELECT CASE 
                WHEN in_social IS NULL THEN FALSE
                WHEN in_sid    IS NULL THEN FALSE
                WHEN in_auth   IS NULL THEN FALSE
                ELSE (MD5('secret word' || in_social || in_sid) = in_auth)
        END;

$func$ LANGUAGE sql IMMUTABLE;