【问题标题】:How to ensure that a stored function always returns TRUE or FALSE?如何确保存储的函数始终返回 TRUE 或 FALSE?
【发布时间】:2016-03-02 09:15:41
【问题描述】:

我想通过以下存储的函数来验证用户数据:

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 对象返回 FALSE,则将调用 RAISE EXCEPTION(从而回滚整个事务)。

我没有在此处转储我的第二个存储函数的源代码,而是在下面准备了 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;

第三个函数没有按预期工作并打印valid user

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

COALESCE 可以包裹在check_user() 语句中的check_user() 调用中...但是是否有更好的方法来解决这个问题?

【问题讨论】:

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


    【解决方案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,当只有一个输入为空时返回 false。因此,这些构造实际上就像 null 是一个正常的数据值一样,而不是“未知”。

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

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

    STRICT 表示只要函数的任何参数为空,该函数总是返回空。如果指定了这个参数,当有空参数时,函数不会被执行;而是自动假定为 null 结果。

    这具有避免执行函数的任何成本的额外好处。

    【讨论】:

    • 在第二条语句中(对于STRICT 函数)是否必须使用is not true 部分?我不能只使用 if NOT check_user(42, 'user1', null) then raise notice 'invalid user'; end if; 吗?谢谢
    • @AlexanderFarber: expression is not true 将为falsenull 返回true,而not expression 只会为false 返回true 而不是null,这是您的原始问题.
    【解决方案2】:

    我会在 check_user() 函数中添加合并,所以它总是返回 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 的值

    【讨论】:

      【解决方案3】:

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

      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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-19
        • 1970-01-01
        • 2016-04-16
        • 1970-01-01
        相关资源
        最近更新 更多