函数参数anyelement,PostgreSQL错误?
我没有看到此实现中的错误:函数参数anyelement,PostgreSQL错误?,sql,postgresql,types,query-planner,polymorphic-functions,Sql,Postgresql,Types,Query Planner,Polymorphic Functions,我没有看到此实现中的错误: 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(
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!
您的问题在于如何规划SQL语句。SQL对于数据类型非常严格。Postgres函数为多态伪类型
ANYELEMENT
提供了一定的灵活性,但是SQL语句仍然是静态地使用给定类型规划的
虽然表达式$1::int>2
在$1
不是整数的情况下永远不会执行(您可以通过这种方式避免被零除),但这无法避免在规划查询的早期阶段出现语法错误
你仍然可以用你的功能做一些事情。使用非类型化字符串文字:
CREATE OR REPLACE FUNCTION foo(anyelement)
RETURNS SETOF int AS
$func$
SELECT id FROM unnest(array[1,2,3]) id
WHERE CASE WHEN pg_typeof($1) = 'integer'::regtype
THEN $1 > '2' -- use a string literal!
ELSE true END
$func$ LANGUAGE sql IMMUTABLE;
而不是:
(pg_typeof($1)::text)='integer'
那通常更好。最好将常量转换一次,而不是每次都转换计算值。这也适用于类型名称的已知别名
它肯定与SQL planner/optimizer有关。由于函数被声明为不可变
,因此优化器尝试对查询部分进行预评估。出于某种原因,即使使用text
参数调用函数,它也会计算表达式$1::int>2
如果将foo
函数更改为VOLATILE
,它将正常工作,因为查询优化器不会尝试对其进行优化/预评估
但是为什么条
函数即使是不可变的也能正常工作呢?我猜优化器决定不预先评估它,因为它不在循环中使用表达式。我的意思是,$1::int>2
只计算一次,而在foo
函数中,它被计算多次
SQL planner在
SQL
和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
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