1

用下面的存儲功能,我想驗證用戶數據:如何確保存儲的函數總是返回TRUE或FALSE?

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數組循環 - 並會RAISE EXCEPTION如果任何返回FALSE的JSON對象(並因此回滾整個事務)。

除了在此處傾倒我的第二個存儲功能的源代碼,我已經準備好以下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; 

第1功能按預期工作,並打印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()調用IF -statement ...但有沒有更好的方法來解決這個問題?

回答

1

檢查is [not] distinct from

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

但請注意,如果雙方評估爲null比較將返回true

對於非空輸入,有別於相同的<>運算符。但是,如果兩個輸入都爲空,則返回false,並且如果只有一個輸入爲空,則返回true。同樣,對於非空輸入,IS NOT DISTINCT FROM與=相同,但當兩個輸入都爲空時它將返回true,而當只有一個輸入爲空時,則返回false。因此,這些結構有效地表現爲空值是正常的數據值,而不是「未知」。

更簡單的是剛剛宣佈的功能strictis not true比較返回值:

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

嚴格表示函數總是返回null每當它的任何參數爲空。如果指定了該參數,那麼當有空參數時,該函數不會被執行;相反,會自動假定爲空結果。

這有避免任何執行該功能的成本的額外好處。

+0

在第二條語句(對於'STRICT'函數)是否必須使用'is not true'部分?我不能只使用'如果不是check_user(42,'user1',null),然後通知'無效用戶';結束如果;'?謝謝 –

+1

@AlexanderFarber:'表達式不是真的'對於'false'和'null'都會返回true,'not表達式'只會對'false'返回'true'而不是'null',這是你原來的問題。 –

1

我會在check_user()函數內添加coalesce,所以它總是返回true或false。

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

甚至跌破的情況下,其它的值​​也可能是空的選項:

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

在那裏「-1」,將永遠不會被分配一個值in_social

0

下面是我可能會用到的,因爲該功能是安全相關的,因此我不想在將來使用它時記住任何邊界情況(如強制使用IF check_user(...) IS NOT TRUE時聲明它爲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;