4

この実装ではバグは見られません。

CREATE FUNCTION foo(anyelement) RETURNS SETOF int  AS $f$
    SELECT id FROM unnest(array[1,2,3]) t(id) 
    WHERE CASE WHEN (pg_typeof($1)::text)='integer' THEN $1::int>2 ELSE true END
$f$ LANGUAGE SQL IMMUTABLE;

SELECT * FROM foo(123); -- OK!
SELECT * FROM foo('test'::text); -- BUG

これはある種の PostgreSQL のバグですか、それとも文書化されていないanyelementデータ型の制限ですか?


興味深い: 句が分離されている場合、このCASE句は正常に機能します。

 CREATE FUNCTION bar(anyelement) RETURNS boolean  AS $f$
   SELECT CASE WHEN (pg_typeof($1)::text)='integer' THEN $1::int>2;
 $f$ LANGUAGE SQL IMMUTABLE;

 SELECT bar('test'::text), bar(123), bar(1); -- works fine! 
4

2 に答える 2

2

これは間違いなく SQL プランナー/オプティマイザーに関連しています。関数は として宣言されているIMMUTABLEため、オプティマイザーはクエリ部分を事前評価しようとします。何らかの理由で、関数をパラメーター$1::int>2で呼び出しても、式を評価します。text

関数を次のように変更すると、クエリ オプティマイザが最適化/事前評価を試行しないため、foo正常に機能します。VOLATILE

しかし、なぜbar機能が正常に機能するのIMMUTABLEですか? オプティマイザーは、ループで式を使用しないため、事前評価しないことを決定したと思います。つまり$1::int>2、それは 1 回だけ評価されるのに対し、foo関数では複数回評価されるということです。


SQLSQLプランナーの動作とPLPGSQL言語にいくつかの違いがあるようです。同じ機能が正常にPLPGSQL動作します。

CREATE FUNCTION foo2(anyelement) RETURNS SETOF int AS $f$
DECLARE 
    i INTEGER;
BEGIN
    FOR i IN SELECT id FROM unnest(array[1,2,3]) t(id) 
        WHERE 
            CASE WHEN pg_typeof($1) = 'integer'::regtype 
                THEN $1::int > 2
                ELSE true END
    LOOP
        RETURN NEXT i;
    END LOOP;
END;
$f$ LANGUAGE plpgsql IMMUTABLE;

SELECT * FROM foo2('test'::text); -- works fine
于 2016-03-16T04:55:18.130 に答える