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)
您的问题是由于 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),则会触发语法错误FROM:SELECT * FROM foo()而不是SELECT foo()。IMMUTABLE对于可以内联的简单函数来说,查询计划的处理方式似乎有所不同。
除此之外,使用:
pg_typeof($1) = 'integer'::regtype
Run Code Online (Sandbox Code Playgroud)
代替:
(pg_typeof($1)::text)='integer'
一般情况下这样比较好。最好只转换一次常量,而不是每次都转换计算值。这也适用于类型名称的已知别名。
| 归档时间: |
|
| 查看次数: |
4231 次 |
| 最近记录: |