Function parameter anyelement, PostgreSQL bug? - sql

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>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.
PostgreSQL syntax error in parameterized query on "date $1"

It's definitely related to SQL planner/optimizer. Since the function is declared as IMMUTABLE, the optimizer tries to pre-evaluate the query parts. For some reason, it evaluates the expression $1::int>2 even if you call the function with text parameter.
If you change your foo function to VOLATILE it will work fine, because the query optimizer will not try to optimize/pre-evalute it.
But why bar function works fine even if it's IMMUTABLE? I guess the optimizer decides not to pre-evalute it as it does not use expressions in loops. What I mean is that $1::int>2 is evaluated only once, whereas in foo function it's evaluated multiple times.
Seems like there are some differences how SQL planner works for SQL and PLPGSQL language. The same function in PLPGSQL works fine.
CREATE FUNCTION foo2(anyelement) RETURNS SETOF int AS $f$
DECLARE
i INTEGER;
BEGIN
FOR i IN SELECT id FROM unnest(array[1,2,3]) t(id)
WHERE
CASE WHEN pg_typeof($1) = 'integer'::regtype
THEN $1::int > 2
ELSE true END
LOOP
RETURN NEXT i;
END LOOP;
END;
$f$ LANGUAGE plpgsql IMMUTABLE;
SELECT * FROM foo2('test'::text); -- works fine

Related

How to handle type conversion error in Postgresql query?

I was facing an error on type conversion in the Oracle server then I found one of the solutions mentioned below:
SELECT TO_NUMBER('b' default -1 on conversion error) from dual;
output: -1
As mentioned in the above query, it will give the default value if the value is non-numeric.
Is there any way to handle the same thing in the Postgres SQL?
As per #a_horse_with_no_name's comment: PostgreSQL doesn't offer a built-in function like that, but it's pretty simple to make one in PL/pgSQL yourself.
CREATE OR replace FUNCTION to_number(
argument text,
default_result integer default -1)
RETURNS integer LANGUAGE plpgsql
AS $$
BEGIN
RETURN argument::integer;
EXCEPTION WHEN OTHERS THEN
RETURN default_result;
END $$;
or, even in plain SQL - as suggested by Vaibhav Parab's comment
CREATE OR replace FUNCTION to_number(argument text,default_result integer default -1)
RETURNS integer LANGUAGE SQL
AS '
SELECT CASE
WHEN argument~E''^\\d+$''
THEN argument::integer
ELSE default_result
END';
Now you can call either by select to_number('b', default_result=>-1);. The language sql function variant does not impose any additional overhead compared to typing out the entire case directly because it meets all the inlining criteria so it performs the same while being shorter.
Here's a working DB<>fiddle.

I'm trying to cross database with insert query in plpgsql, please help me

CREATE OR REPLACE FUNCTION "public"."cross_insert"("p_name" varchar, "p_detail" varchar)
RETURNS SETOF "pg_catalog"."varchar" AS $BODY$
BEGIN
SELECT * FROM public.dblink(
'
host=10.10.10.53
port=5432
user=sassuperuser
password=password10
dbname=blog2
',
'
SELECT * FROM public.funct_insert2(
'''||p_name||''',
'''||p_detail||'''
);
'
);
RETURN query
SELECT ('SUKSES')::character varying;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000
Your code is little bit messy. Try start with reading documentation to PLpgSQL. There are three issues:
PL/pgSQL doesn't allow implicit throwing result of any query. You should to iterate over result (FOR IN SELECT) or you have to save result to variable (SELECT INTO). If you want to do it, then you should to use PERFORM command. So you query should to look like:
PERFORM public.dblink(' .....');
There is not any sense why your function is declared like RETURNS SETOF varchar. Then you have to use RETURN QUERY SELECT 'success'. It is absolutely useless. There is relatively high overhead of this functionality. This should be classic scalar function that returns text or better, that returns void type. The exception is raised on error. So you don't need to returns anything.
Second, don't do this - PostgreSQL has FDW interface. You can use foreign table instead. It will be faster and safer.

Create function in PostgreSQL

I am not sure how the CREATE FUNCTION statement works in PostgreSQL. I want to define a function (just for entertainment) such that given a number n, it prints asterisks starting from 1 up to n
So I wrote this:
CREATE FUNCTION asterisks(n int)
RETURNS CHAR AS
BEGIN
for i in range(1,n+1):
print("*"*i + "\n")
END
LANGUAGE python
The result I want for n=3:
*
**
***
However, I am not sure if calling Python like that is possible. I've read that Postgres supports Python as a procedural language in here:
https://www.postgresql.org/docs/current/xplang.html
Postgres 14 or later
The simplest way would be with the new standard SQL syntax:
CREATE OR REPLACE FUNCTION asterisks(n int)
RETURNS SETOF text
RETURN repeat('*', generate_series (1, n));
Or better (and all standard SQL):
CREATE OR REPLACE FUNCTION asterisks(n int)
RETURNS SETOF text
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
BEGIN ATOMIC
SELECT repeat('*', g) FROM generate_series (1, n) g;
END;
"Better" because it's easier to understand, sticks to standard SQL (more portable). Both debatable. And it sets IMMUTABLE STRICT PARALLEL SAFE appropriately, which would otherwise default to VOLATILE CALLED ON NULL INPUT PARALLEL UNSAFE. Non-debatable.
Call:
SELECT asterisks(6);
Or, more explicitly and standard-conforming:
SELECT * FROM asterisks(6);
See:
generate_series() in the Postgres manual
Any downsides of using data type "text" for storing strings?
What does BEGIN ATOMIC ... END mean in a PostgreSQL SQL function / procedure?
Postgres 13 (or any version):
SQL function:
CREATE OR REPLACE FUNCTION asterisks(n int)
RETURNS SETOF text
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
$func$
SELECT repeat('*', generate_series (1, n));
$func$;
PL/pgSQL function with loops (looping is typically more expensive):
CREATE OR REPLACE FUNCTION pg_temp.asterisks(n int)
RETURNS SETOF text
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE AS
$func$
BEGIN
FOR i IN 1..n LOOP
RETURN NEXT repeat('*', i);
END LOOP;
END
$func$;
See:
Difference between language sql and language plpgsql in PostgreSQL functions
Of course, for the simple example, I would just run the plain statement instead of creating a function:
SELECT repeat('*', generate_series (1, 3));

how to use argument as table name in a postgresql SQL function

I've got a function to return relevant position of an array matching given value like below:
CREATE OR REPLACE FUNCTION get_index(needle ANYELEMENT, haystack ANYARRAY)
RETURNS TABLE (i_position int)
AS $$
SELECT
i+i_step AS i_position
FROM (VALUES(1),(2)) steps(i_step),
generate_series(array_lower($2,1), array_upper($2,1)) AS i
WHERE $2[i] = $1
$$ LANGUAGE SQL STABLE;
Instead of passing a single value to the function, I want to pass a table name, as one column of the table would be used to do the value comparison (WHERE $2[i] = $1 ), instead of a single value passed to the function. However, it doesn't seem like the function support SQL using argument as table name.
I'm wondering if there's alternative. I'd like to use SQL function instead of PLPGSQL, for the sake of performance. As our table is huge.
I'd like to achieve something like below:
CREATE OR REPLACE FUNCTION get_index(tbl ANYELEMENT, haystack ANYARRAY)
RETURNS TABLE (i_position int)
AS $$
SELECT
i+i_step AS i_position
FROM (VALUES(1),(2)) steps(i_step),
generate_series(array_lower($2,1), array_upper($2,1)) AS i,
$1
WHERE $2[i] = $1.col1
$$ LANGUAGE SQL STABLE;
It is not possible in SQL function - It doesn't support dynamic sql. You have to use PLpgSQL - EXECUTE statement.
SQL function is faster than PLpgSQL only when inlining is successful. When it isn't successful, and SQL function should be evaluated, then PLpgSQL should not be slower. When body of SQL function contains SRF functions like generate_series, then inlining is not effective.

PostgreSQL Math Function Error

I'm getting an error from a deterministic PG function that seems dependent based on when I call it in a query.
The function is:
CREATE OR REPLACE FUNCTION ircm(starting_money numeric, value numeric, days numeric)
RETURNS numeric
AS $$
BEGIN
-- Calculate effective interest rate, compounded monthly.
RETURN 12*(pow((value/starting_money),(1./(12.*(days/365.)))) - 1);
END;
$$ LANGUAGE plpgsql;
If I call it in a simple SELECT statement, it works fine:
SELECT ircm(100.00,60.427500643787215,30)
Result: -4.79925436505569765596
However, when I run that exact same call from a different SELECT statement using a subquery:
SELECT
ircm(100.00,m.v::numeric,30) AS result
FROM(
SELECT 60.427500643787215 AS v
) m
I get the error:
ERROR: a negative number raised to a non-integer power yields a complex result
CONTEXT: PL/pgSQL function "ircm" line 6 at RETURN
Since the calls are logically equivalent, how can PG be returning an error for one call, but not the other? I've made sure there's only one definition of the function "ircm" in my database. I've tried dropping/adding it again to make sure PG isn't somehow caching a corrupted definition.
Which version and on which platform is your database running?
Have you tried rewriting the function in SQL:
CREATE OR REPLACE FUNCTION ircm(numeric, numeric, numeric)
RETURNS numeric AS $$
select 12 * (pow(($2 / $1), (1. / (12.* ($3 / 365.)))) - 1);
$$ LANGUAGE sql immutable;
I'm running v8.4.4 on Solaris, Linux, and Mac OS X and achieve the same results from your plpgsql (and my SQL) version called directly, and from within the subquery.