我没有看到这个实现中的错误:
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
提供了一定的灵 active ,但 SQL 语句仍然使用给定的类型进行静态规划。
如果$1
不是整数
,则表达式$1::int>2
永远不会执行(您可以通过这种方式避免被零除),这不能使您免于在查询的早期规划阶段出现的语法错误。
你仍然可以用你拥有的功能做一些事情。使用未类型化的字符串文字:
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 <b>$1 > '2'</b> -- use a string literal!
ELSE true END
$func$ LANGUAGE sql IMMUTABLE;
这至少适用于所有字符和数字数据类型。字符串文字被强制转换为提供的数据类型。但对于“2”无效的其他数据类型,它仍然会失败。
您的第二个示例没有触发语法错误,这值得注意。从我对 Postgres 9.5 的测试中可以看出,如果函数不是 IMMUTABLE
或设置返回函数(RETURNS SETOF ...
而不是 ,则会触发语法错误>RETURNS boolean
) 在 FROM
列表中调用:SELECT * FROM foo()
而不是 SELECT foo()
。对于可以内联的简单 IMMUTABLE
函数,查询计划的处理方式似乎有所不同。
除此之外,使用:
pg_typeof($1) = 'integer'::regtype
代替:
(pg_typeof($1)::text)='integer'
这通常更好。每次都转换常量 一次 而不是计算值总是更好。这也适用于类型名称的已知别名。
关于sql - 函数参数anyelement,PostgreSQL bug?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36025889/