postgresqlのレコード型変数のNULLチェック

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を代入したときのようなエラーは発生しない。

```sql
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
```