How do I create a nested function in PL/pgSQL? - postgresql

I would like to create a function in PL/pgSQL with a couple of nested (or inner) functions within it. This way I can break the problem down into smaller pieces but not have my smaller pieces accessible outside of this function.
Is it possible to do this in PL/pgSQL? If so, how?

Try it:
CREATE OR REPLACE FUNCTION outer() RETURNS void AS $outer$
DECLARE s text;
BEGIN
CREATE OR REPLACE FUNCTION inner() RETURNS text AS $inner$
BEGIN
RETURN 'inner';
END;
$inner$ language plpgsql;
SELECT inner() INTO s;
RAISE NOTICE '%', s;
DROP FUNCTION inner();
END;
$outer$ language plpgsql;
In postgres 9.5 SELECT outer(); outputs
psql:/vagrant/f.sql:14: NOTICE: inner
EDIT: if you don't drop the inner
function at the end of the outer function it will remain visible to the rest of the database.

Nested functions are not supported by PLpgSQL. The emulation has not any sense and it is nonproductive.

Related

Create a procedure/function that creates new sequence

I need to create a new procedure/function in Postgres that creates a new sequence.
The procedure/function will get the name of the sequence as a variable and creates it.
I tried to follow the documentation but it's not very helpful. This is what I've got so far but it's not working (of course):
CREATE FUNCTION create_seq(text) RETURNS text
AS 'CREATE SEQUENCE $1 START 100;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
ERROR: return type mismatch in a function declared to return text
DETAIL: Function's final statement must be SELECT or INSERT/UPDATE/DELETE RETURNING.
CONTEXT: SQL function "create_seq"```
you need use dynamic SQL like said #a_horse_with_no_name, but you need use the plpgsql language not sql language for the function, for remember return void,example,:
CREATE FUNCTION create_seq_plpgsql(p_seq_name text)
RETURNS void
AS
$$
begin
execute format('CREATE SEQUENCE %I START 100', p_seq_name);
end;
$$
LANGUAGE plpgsql;
You need dynamic SQL for that, parameters can't be used as identifiers.
To properly deal with names that potentially need quoting, it is highly recommended to use the format() function to generate the SQL.
And you need to declare the function as returns void as you don't want to return anything.
CREATE FUNCTION create_seq(p_seq_name, text)
RETURNS void
AS
$$
begin
execute format('CREATE SEQUENCE %I START 100', p_seq_name);
end;
$$
LANGUAGE plpgsql;
If you are on Postgres 11, you could also use a procedure instead of a function for that.

How to declare a function in postgresql? [duplicate]

I would like to create a function in PL/pgSQL with a couple of nested (or inner) functions within it. This way I can break the problem down into smaller pieces but not have my smaller pieces accessible outside of this function.
Is it possible to do this in PL/pgSQL? If so, how?
Try it:
CREATE OR REPLACE FUNCTION outer() RETURNS void AS $outer$
DECLARE s text;
BEGIN
CREATE OR REPLACE FUNCTION inner() RETURNS text AS $inner$
BEGIN
RETURN 'inner';
END;
$inner$ language plpgsql;
SELECT inner() INTO s;
RAISE NOTICE '%', s;
DROP FUNCTION inner();
END;
$outer$ language plpgsql;
In postgres 9.5 SELECT outer(); outputs
psql:/vagrant/f.sql:14: NOTICE: inner
EDIT: if you don't drop the inner
function at the end of the outer function it will remain visible to the rest of the database.
Nested functions are not supported by PLpgSQL. The emulation has not any sense and it is nonproductive.

SELECT usage with the new CREATE PROCEDURE method

I'm trying to store a simple SELECT query with the new CREATE PROCEDURE method in PostgreSQL 11. My idea is to store the queries in the DB, because I can have a much simple code in my API server and maybe I don't need to develop a query builder if I can use if/else in an sql function with enforced type safety. I have this minimal example:
First I tried this plpgsql function:
CREATE OR REPLACE PROCEDURE test_proc() AS $$
BEGIN
SELECT * FROM my_db
LIMIT 1;
END;
$$ LANGUAGE plpgsql;
CALL test_proc();
However throws this error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function test_proc() line 3 at SQL statement SQL state: 42601
If I trying to use RETURN QUERY:
CREATE OR REPLACE PROCEDURE test_proc() AS $$
BEGIN
RETURN QUERY;
SELECT * FROM my_db
LIMIT 1;
END;
$$ LANGUAGE plpgsql;
I'm getting this error:
ERROR: cannot use RETURN QUERY in a non-SETOF function
LINE 17: RETURN QUERY; ^
SQL state: 42804
Character: 310
I'm also getting error when I try to use RETURNS void AS $$ or RETURNS table(...) AS $$. Seems like RETURNS not supported in CREATE PROCEDURE? So, is it possible to return a table with the new stored procedure method? Or if it's not, maybe JSON?
Procedures in PostgreSQL (Oracle, DB2) are not same like procedures in MS-SQL. It has different target, and you cannot use it. Usually, the best what you can do, forgot all what you know from MSSQL. The procedural part is really different.
Only functions can returns some data - so you need to use functions. Functions can returns scalar value, composite value or array value, or table. You want function that returns table.
CREATE OR REPLACE FUNCTION fx()
RETURNS SETOF mytab AS $$
BEGIN
RETURN QUERY SELECT * FROM mytab;
END
$$ LANGUAGE plpgsql;
SELECT * FROM fx();
For record:
You can use SQL function, that can have better (or worse) performance (depends on context). These functions are sometimes named as parametrized views.
CREATE OR REPLACE FUNCTION fx()
RETURNS SETOF mytab AS $$
SELECT * FROM mytab;
$$ LANGUAGE sql;
Attention: this technique is antipattern!!! Don't do it. It is really not good idea. The functions should not to wrap queries. If you want to hide some complexity of queries, then use a views. Don't use a functions. Functions are effective barier for query optimizer, and when you use this antipattern, then optimizer cannot to well optimize any non trivial queries that use in this form evaluated subqueries.
Use it - if you want very very slow applications - or if your data model or queries are primitive. In other cases, don't do it.
Don't afraid of SQL - it is great language designed for manual usage. It is good to place all data access to one module (model), to don't access database everywhere in your code, but it is bad too hide SQL in your code.
First of all Procedure was introduced in PostgreSQL 11, If you are using below 11th version, you cannot use Procedures. Instead to Procedure you can use functions.
Syntax to create function
CREATE or replace function function_name(_parameter varchar)
returns table(col1 varchar, col2 varchar, col3 varchar)
language 'plpgsql'
as $BODY$
BEGIN
return query select a.col1, a.col2, b.col3 from table a
join table2 as b on a.col1 = b.col1;
END;
$BODY$;
you can call a function same a like table
select * From function_name('sample data');
syntax to create Procedure.
CREATE OR REPLACE PROCEDURE procedure_name(_parameter varcar,INOUT result refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
open result for SELECT , * from sampletable where a = _parameter;
END;
$BODY$;
you can execute a Procedure using call keyword, within a transaction
BEGIN;
CALL public.procedure_name( 'sample data', 'test');
fetch all in "test";
COMMIT;
The postgreSql 11. we have to create a stored procedure
there is the solution :
Create procedure to execute query in PostgreSQL

"perform create index" in plpgsql doesn't run

I'm having problems executing a "perform create index" inside of a plgpsql function (postgres 9.4). For example:
create or replace function foo() returns void language plpgsql as $$
begin
perform 'create unique index patients_row_id_key on patients(row_id)';
end; $$;
It seems to run fine:
select foo();
However, the index is not created. Any diagnosis and workaround? I tried:
alter function foo() VOLATILE;
and still no luck.
What #Abelisto wrote about PERFORM.
And what #Chris added about SQL injection.
Plus, I suggest to use format() for anything except the most trivial query strings to make your life with dynamic SQL easier. And the manual does, too:
A cleaner approach is to use format()'s %I specification for table or column names.
CREATE OR REPLACE FUNCTION foo(_tbl text)
RETURNS void AS
$func$
BEGIN
EXECUTE format('CREATE UNIQUE INDEX %I ON %I(row_id)', _tbl || _row_id_key', _tbl);
END
$func$ LANGUAGE plpgsql;
A regclass parameter is a convenient alternative for passing table names, but concatenating new identifiers can be tricky - as this recent related case goes to show:
PL/pgSQL regclass quoting of table named like keyword
As a supplement to the point of using execute, note two important points about this.
You are doing string interpolation with sql queries (dangerous!), and
You have to use quote_ident, not quote_literal
If you use Abelisto's function above, and call it with:
SELECT foo('test_idx on test; drop table foo; --');
SQL injection in stored procedure. Worse if it is security definer. A fixed version would be:
create or replace function foo(p_tablename text) returns void language plpgsql as $$
begin
execute 'create unique index ' || quote_ident(p_tablename || '_row_id_key') || ' on ' || quote_ident(p_tablename) || '(row_id)';
end; $$;
PERFORM statement in the PLPGSQL used to execute queries which does not return result or which result is not useful. Technically PERFORM ... inside the PLPGSQL block is equal to SELECT ... in the plain SQL. So in your example you are trying to execute something like
select 'create unique index patients_row_id_key on patients(row_id)';
and just ignore the result.
Read more: Executing a Command With No Result
You should not to wrap DDL statements inside PLPGSQL and can use it as is:
create or replace function foo() returns void language plpgsql as $$
begin
create unique index patients_row_id_key on patients(row_id);
end; $$;
Or if you want to construct it at runtime then use EXECUTE statement: Executing Dynamic Commands like this:
create or replace function foo(p_tablename text) returns void language plpgsql as $$
begin
execute 'create unique index ' || p_tablename || '_row_id_key on ' || p_tablename || '(row_id)';
end; $$;

Return value from anonymous function postgresql

How to?
For easy example. I have a simple function:
DO LANGUAGE plpgsql $$ DECLARE
BEGIN
EXECUTE 'SELECT NOW()';
END $$;
How I can return value of "NOW()" or other values from also anonymous function? The function is given as an example I have a more complex function.
DO LANGUAGE plpgsql $$ DECLARE
BEGIN
execute '
create temporary table t
as
SELECT NOW()
';
END $$;
select * from t;
It is not an anonymous function, but rather anonymous code block.
if you need to return values, consider creating real functions;
if you need to output some debug info, just RAISE NOTICE.