Create function in PostgreSQL - sql

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));

Related

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.

Function parameter anyelement, PostgreSQL bug?

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

Passing multiple values in single parameter

Let's say I have this function:
CREATE OR REPLACE FUNCTION test_function(character varaying)
RETURNS integer AS
$BODY$
DECLARE
some_integer integer;
begin
Select column2 from test_table where column1 in ($1) into some_integer;
end;
Return some_integer;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
And I want to call it like this:
Select * from test_function ('data1', 'data2','data3');
Of course, it cannot be done this way, because Postgres tries to find function with this name and three parameter which doesn't exists.
I tried to put quotes around commas but in that case parameter is interpreted wrong: data1', 'data2','data3, like one string.
Is there a way to put multiple values in parameter so IN clause can recognized it?
(Your function wouldn't be created. RETURN after END is syntactical nonsense.)
A function with a VARIADIC parameter does exactly what you ask for:
CREATE OR REPLACE FUNCTION test_function(date, date, VARIADIC varchar[])
RETURNS SETOF integer
LANGUAGE sql AS
$func$
SELECT col1
FROM test_table
WHERE col3 > $1
AND col4 < $2
AND col2 = ANY($3)
$func$;
db<>fiddle here - demo with additional parameters
Old sqlfiddle
Call (as desired):
SELECT * FROM test_function('data1', 'data2', 'data3');
Using a simple SQL function, plpgsql is not required for the simple example. But VARIADIC works for plpgsql functions, too.
Using RETURNS SETOF integer since this can obviously return multiple rows.
Details:
Pass multiple values in single parameter
Return rows matching elements of input array in plpgsql function
VARIADIC parameter must be the last input parameter
Return rows matching elements of input array in plpgsql function

Executing a list of PostgreSQL queries returned as result of a SELECT statement [duplicate]

I want to do like this:
SELECT (EVAL 'SELECT 1') + 1;
Are there any way to do like this (EVAL) in PostgreSQL?
If the statements you are trying to "eval" always return the same data type, you could write an eval() function that uses the EXECUTE mentioned by Grzegorz.
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute expression into result;
return result;
end;
$body$
language plpgsql
Then you could do something like
SELECT eval('select 41') + 1;
But this approach won't work if your dynamic statements return something different for each expression that you want to evaluate.
Also bear in mind that this opens a huge security risk by running arbitrary statements. If that is a problem depends on your environment. If that is only used in interactive SQL sessions then it isn't a problem.
NOTES
The language PLpgSQL syntax have many ways to say:
Y := f(X);
The EXECUTE clause is only for "dynamic execution" (less performance),
EXECUTE 'f(X)' INTO Y;
Use Y := f(X); or SELECT for execute static declarations,
SELECT f(X) INTO Y;
Use PERFORM statment when discard the results or to work with void returns:
PERFORM f(X);
I'd go with data type text since it's more flexible using casting operators like ::int if needed:
create or replace function eval( sql text ) returns text as $$
declare
as_txt text;
begin
if sql is null then return null ; end if ;
execute sql into as_txt ;
return as_txt ;
end;
$$ language plpgsql
-- select eval('select 1')::int*2 -- => 2
-- select eval($$ select 'a'||1||'b' $$) -- => a1b
-- select eval( null ) -- => null
I also added this and another eval( sql, keys_arr, vals_arr ) function supporting some custom key-value substitutions, e.g. for handy :param1 substitutions to postgres-utils
I am not sure if it suits you but PostgreSQL has EXECUTE statement.
Good idea. You can modify to perform direct expressions:
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute 'SELECT ' || expression into result;
return result;
end;
$body$
language plpgsql;
To run just type this:
SELECT eval('2*2');
Assuming that most sql queries are a part of a bigger system, there mostly will be cases where you form a query with your backend code and then execute it.
So if that’s the case for you, you can just use subselects or common table expressions that are put into your query string by the backend code before execution.
I have trouble coming up with cases where the solution you want works and my solution doesn’t, apart from not having any backend app, of course.

Writing a Procedure in PostgreSQL 9.1

I am trying to write a procedure (function) in Postgres 9.1 to just select all data from a view and return it (all columns) so our application can use it. The intent is to have the application make a call to the procedure whenever it wants the data. My problem is that I cannot seem to get the syntax right to have it return any data. I have written procedures in MySQL, SQL Server and Oracle but never in postgres and it is quite different. Once it works is the best way to call it to just use select "function-name"? Any help would be appreciated.
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record AS
$BODY$
declare times record;
begin
select * into times from fg.fn_dy_dest_dep_arr;
return;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION fg.get_flight_times()
OWNER TO jason;
GRANT EXECUTE ON FUNCTION fg.get_flight_times() TO fltrakr;
Inside a PL/pgSQL function you need to use RETURN NEXT or RETURN QUERY as documented in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#AEN54092
User RETURNS setof record is no longer necessary in 9.1, using returns table is a lot easier. If you only want to return the result of SELECT statement, you can use SQL as well, which makes the function even easier:
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS table (col1 integer, col2 text, col3 text)
$BODY$
select col1, col2, col3 from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
Note that in this case, you have to specify the column definition returned by your function at creation time. With that version you just use:
select * from fg.get_flight_times();
When using returns setof record you don't need to specify the columns at creation time, but you will be forced to do so when selecting from the function (which I find more complicated, as this needs to be done everytime the function is used):
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record
$BODY$
select * from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
And this is used like this:
select *
from fg.get_flight_times() f(col1 integer, col2 text, col3 text);
Much more complicated in my opinion (because the caller needs to know the structure and datatypes of the columns)
Do read the manual, everything I have written here is documented there as well (I know it only because I have already read the manual...)