I do not see the bug in this implementation:
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
Is this some kind of PostgreSQL bug or a non-documented restriction on anyelement datatype?
Interesting: when isolated the CASE clause works fine:
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!
Your problem is due to how SQL statements are planned. SQL is very rigid about data types. Postgres functions provide some flexibility with the polymorphic pseudo-type
ANYELEMENT, but the SQL statement is still planned with the given types statically.While the expression
$1::int>2is never executed if$1is not aninteger(you could avoid division by zero this way), this cannot save you from the syntax error that arises at the earlier stage of planning the query.You can still do something with the function you have. Use an untyped string literal:
This at least works for all character and numeric data types. The string literal is coerced to the provided data type. But it will still fail for other data types where '2' is not valid.
It's remarkable that your second example does not trigger the syntax error. It emerged from my tests on Postgres 9.5 that the syntax error is triggered if the function is not
IMMUTABLEor for set-returning functions (RETURNS SETOF ...instead ofRETURNS boolean) that are called in theFROMlist:SELECT * FROM foo()instead ofSELECT foo(). It would seem that query planning is handled differently for simpleIMMUTABLEfunctions which can be inlined.Aside, use:
instead of:
(pg_typeof($1)::text)='integer'That's generally better. It's always better to cast the constant once instead of the computed value every time. And this works for known aliases of the type name as well.