函数参数anyelement,PostgreSQL bug?

Pet*_*uss 5 sql postgresql types polymorphic-functions query-planner

我没有看到这个实现中的错误:

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
Run Code Online (Sandbox Code Playgroud)

这是某种 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! 
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 4

您的问题是由于 SQL 语句的规划方式造成的。SQL 对于数据类型非常严格。Postgres 函数通过多态伪类型提供了一些灵活性ANYELEMENT,但 SQL 语句仍然使用给定类型静态规划。

虽然如果不是 an,$1::int>2则永远不会执行表达式(您可以通过这种方式避免被零除),但这无法使您避免在规划查询的早期阶段出现的语法错误。$1integer

你仍然可以用你拥有的功能做一些事情。使用无类型字符串文字:

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;
Run Code Online (Sandbox Code Playgroud)

这至少适用于所有字符和数字数据类型。字符串文字被强制为提供的数据类型。但对于“2”无效的其他数据类型,它仍然会失败。

值得注意的是,您的第二个示例不会触发语法错误。从我对 Postgres 9.5 的测试中发现,如果函数不是IMMUTABLE或在列表中调用的设置返回函数(RETURNS SETOF ...而不是RETURNS boolean),则会触发语法错误FROMSELECT * FROM foo()而不是SELECT foo()IMMUTABLE对于可以内联的简单函数来说,查询计划的处理方式似乎有所不同。


除此之外,使用:

pg_typeof($1) = 'integer'::regtype
Run Code Online (Sandbox Code Playgroud)

代替:

(pg_typeof($1)::text)='integer'

一般情况下这样比较好。最好只转换一次常量,而不是每次都转换计算值。这也适用于类型名称的已知别名。