postgresql 9.6で確認。
postgresqlの関数で、レコード型の変数を用意して、
そのレコード型変数自体を、IS NULLと、IS NOT NULLで判断したとき、
どうなるか調べてみた。
結果、以下のような判断となった。
・IS NULLとした場合、レコードが持つ列すべてがNULLだったら真、それ以外だったら偽(1つでも値がNULLでなければ偽)
・IS NOT NULLとした場合、レコードが持つ列すべてが非NULLだったら真、それ以外だったら偽(1つでも値がNULLだったら偽)
また、NULLのチェックをするときに、レコード型の変数自体がNULLだった場合、エラーが発生する。
レコード型が持っている列が確定していないと、チェックができない、ということらしい。
SELECT INTOで代入しようとしたとき、SELECTの結果が1行もない場合は、
レコード型変数の構造自体は決定するが、列すべてはNULL値となっている。
なので、レコード型変数自体にNULLを代入したときのようなエラーは発生しない。
CREATE OR REPLACE FUNCTION public.test_record()
RETURNS integer AS
$BODY$
DECLARE
rec RECORD;
BEGIN
-- 何も代入していないレコード型変数に対して、IS NULLを見ると、以下のエラーが発生する。
-- ERROR: record "rec" is not assigned yet
-- DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
/*
IF rec IS NULL THEN
RAISE INFO 'rec is null';
ELSE
RAISE INFO 'rec is not null';
END IF;
*/
-- 2つのカラムを持つレコードを代入。うち、1つのカラムはNULLとした。
SELECT INTO rec
'test' as name
,NULL as val
WHERE
true
;
RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;
-- この結果は偽となる。
-- rec.nameが非NULLなので。
IF rec IS NULL THEN
RAISE INFO 'rec IS NULL -> true';
ELSE
RAISE INFO 'rec IS NULL -> false';
END IF;
-- この結果は偽となる。(自分は、この結果で少しハマった。レコードあるのに、NOT NULLが偽?、、という具合に)
-- rec.valがNULLなので。
IF rec IS NOT NULL THEN
RAISE INFO 'rec IS NOT NULL -> true';
ELSE
RAISE INFO 'rec IS NOT NULL -> false';
END IF;
-- 先ほど代入したカラムのうち、NULLではなかったカラムにNULLを代入。
rec.name := NULL;
RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;
-- この結果は真となる。
-- すべての列がNULLなので。
IF rec IS NULL THEN
RAISE INFO 'rec IS NULL -> true';
ELSE
RAISE INFO 'rec IS NULL -> false';
END IF;
-- この結果は偽となる。
-- すべての列がNULLなので。
IF rec IS NOT NULL THEN
RAISE INFO 'rec IS NOT NULL -> true';
ELSE
RAISE INFO 'rec IS NOT NULL -> false';
END IF;
-- 両方のカラムに値を代入
rec.name := 'test';
rec.val := '1';
RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;
-- この結果は偽となる。
-- すべての列が非NULLなので。
IF rec IS NULL THEN
RAISE INFO 'rec IS NULL -> true';
ELSE
RAISE INFO 'rec IS NULL -> false';
END IF;
-- この結果は真となる。
-- すべての列が非NULLなので。
IF rec IS NOT NULL THEN
RAISE INFO 'rec IS NOT NULL -> true';
ELSE
RAISE INFO 'rec IS NOT NULL -> false';
END IF;
-- レコード型変数自体に、NULLを代入。
rec := NULL;
-- レコード型変数に対してNULLを代入してからIS NULLを見ると、以下のエラーが発生する。
-- ERROR: record "rec" is not assigned yet
-- DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
/*
IF rec IS NULL THEN
RAISE INFO 'rec is null';
ELSE
RAISE INFO 'rec is not null';
END IF;
*/
-- 2つのカラムを持つ問い合わせで、結果が0行だった、という結果を代入する。
SELECT INTO rec
'test' as name
,NULL as val
WHERE
false
;
RAISE INFO 'rec.name = %, rec.val = %', rec.name, rec.val;
-- この結果は真となる。
-- すべての列がNULLなので。
IF rec IS NULL THEN
RAISE INFO 'rec IS NULL -> true';
ELSE
RAISE INFO 'rec IS NULL -> false';
END IF;
-- この結果は偽となる。
-- すべての列がNULLなので。
IF rec IS NOT NULL THEN
RAISE INFO 'rec IS NOT NULL -> true';
ELSE
RAISE INFO 'rec IS NOT NULL -> false';
END IF;
RETURN 0;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ログの出力結果
< 2017-09-22 12:31:53.503 JST > INFO: rec.name = test, rec.val = <NULL>
< 2017-09-22 12:31:53.503 JST > INFO: rec IS NULL -> false
< 2017-09-22 12:31:53.503 JST > INFO: rec IS NOT NULL -> false
< 2017-09-22 12:31:53.503 JST > INFO: rec.name = <NULL>, rec.val = <NULL>
< 2017-09-22 12:31:53.504 JST > INFO: rec IS NULL -> true
< 2017-09-22 12:31:53.504 JST > INFO: rec IS NOT NULL -> false
< 2017-09-22 12:31:53.504 JST > INFO: rec.name = test, rec.val = 1
< 2017-09-22 12:31:53.504 JST > INFO: rec IS NULL -> false
< 2017-09-22 12:31:53.504 JST > INFO: rec IS NOT NULL -> true
< 2017-09-22 12:31:53.505 JST > INFO: rec.name = <NULL>, rec.val = <NULL>
< 2017-09-22 12:31:53.505 JST > INFO: rec IS NULL -> true
< 2017-09-22 12:31:53.505 JST > INFO: rec IS NOT NULL -> false