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>2
is never executed if $1
is not an integer
(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:
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;
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 IMMUTABLE
or for set-returning functions (RETURNS SETOF ...
instead of RETURNS boolean
) that are called in the FROM
list: SELECT * FROM foo()
instead of SELECT foo()
. It would seem that query planning is handled differently for simple IMMUTABLE
functions which can be inlined.
Aside, use:
pg_typeof($1) = 'integer'::regtype
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.