PostgreSql SET configuration_parameter from Stored Procedure - postgresql

I am implementing Row Level Security (RLS) on a postgres table. To use a single user, I am setting a configuaration_parameter for each session that maps to the row's identity and it works. I can test this using SQL but since I am using Mulesoft for the application, I don't have control over setting up connections and setting the parameter then. I tried to create a stored procedure where I try to set the value in the beginning but postgres doesn't like it. The docs don't share whether I can implement it in the SP or not so wondering if you guys know for sure so I can look at alternate solutions.
https://www.postgresql.org/docs/12/sql-set.html
The below does not work if I uncomment the line SET doc.provider=$1;
CREATE OR REPLACE PROCEDURE getParticipants(text)
LANGUAGE plpgsql
AS $$
BEGIN
--SET doc.provider=$1;
SELECT * FROM disclosure.participantsxref;
END;
$$;

Statement SET doesn't allow parametrization.Instead you can use a function set_config:
CREATE OR REPLACE FUNCTION foo(text)
RETURNS SETOF disclosure.participantsxref AS $$
BEGIN
PERFORM set_config('doc.provider', $1, true);
RETURN QUERY SELECT * FROM disclosure.participantsxref;
END $$
LANGUAGE plpgsql;
In your example, there is another issue - PostgreSQL's procedures cannot to returns tables - if you want to return table, then you have to use function. Functions can return tables with statement RETURN QUERY.

You can try to use dynamic SQL :
create or replace procedure myproc ()
as $$
begin
execute format('set lc_numeric=%L', 'fr_FR') ;
end;
$$
language 'plpgsql';
CREATE PROCEDURE
show lc_numeric;
lc_numeric
-------------
en_US.UTF-8
(1 row)
call myproc();
CALL
show lc_numeric;
lc_numeric
------------
fr_FR
(1 row)

Related

Can PostgreSQL declare a procedure inside a procedure?

create or replace procedure procedure_1()
language plpgsql
as $$
declare
precedure procedure_2()
begin
select 1;
end
begin
select 1;
end; $$
Is there any way to declare a procedure_2() inside procedure_1()?
Yes - if by "declare" you mean "create".
CREATE OR REPLACE PROCEDURE procedure_1(INOUT result int)
LANGUAGE plpgsql AS
$proc1$
BEGIN
CREATE OR REPLACE PROCEDURE procedure_2(INOUT result int)
LANGUAGE plpgsql AS
$proc2$
BEGIN
result := 2;
END
$proc2$;
result := 1;
END
$proc1$;
db<>fiddle here
You just have to get the quoting right. See:
What are '$$' used for in PL/pgSQL
Insert text with single quotes in PostgreSQL
Functions and procedures are not "declared", but "created" in Postgres. That creates an object in the database which is then visible and usable by all with appropriate permissions. (Not just a temporary object local to the procedure or transaction.)
You can, however, create a "temporary" function or procedure, with this "hack" - if that's what you had in mind:
How to create a temporary function in PostgreSQL?

how can I create a stored procedure sql workbench that uses a redshift database?

is it possible to create a stored procedures on sql workbench that uses a redshift database ?
I tried to put in some procedure found on the internet like this one
CREATE OR REPLACE FUNCTION proc_sample RETURN INTEGER
IS
l_result INTEGER;
BEGIN
SELECT max(col1) INTO l_result FROM sometable;
RETURN l_result;
END;
but I get an error
the cursor is not located inside a statement
help please.
Here is my translation of your stored procedure for Redshift:
CREATE OR REPLACE PROCEDURE proc_sample (
l_result OUT INTEGER
)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT max(col1) INTO l_result FROM sometable;
END
$$;
You call this stored procedure in Redshift as follows:
BEGIN; CALL proc_sample(); END;
-- l_result
-- ----------
-- 99
For more information see "Overview of stored procedures in Amazon Redshift"
you can not use from clause in function. you have to use procedure having parameter with out clause.

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

Create temp table in a STABLE stored procedure in postgresql

I would like to create a temp table in a stored procedure which has a STABLE volatility category setted to store the result of a select for later usage in the stored procedure. At the end of the stored procedure this temp table is deallocated and i am sure that this temp table does not have any affect on the database, because as far as i know with this volatility category i ensure the optimizer that this stored procedure will not affect the database.
So i would like to do something like this:
Create a stored procedure which returns with a query:
CREATE OR REPLACE FUNCTION storedproc()
RETURNS TABLE
(Egy TEXT,
Ketto TEXT)
AS $$
BEGIN
RETURN QUERY SELECT * FROM temptable;
END;
$$ LANGUAGE plpgsql;
Create a stored procedure which is using the previous query:
CREATE OR REPLACE FUNCTION stablefunction()
RETURNS TABLE
(Egy TEXT,
Ketto TEXT)
AS $$
BEGIN
-- I would like to store the results here for later usage
CREATE TEMP TABLE buba AS select * from storedproc();
-- Do other stuff
-- ...
-- Reuse the results here which was stored before
END;
$$ LANGUAGE plpgsql
STABLE;
But when i want to execute this stored procedure as this:
DO
$$
BEGIN
perform stablefunction() ;
END;
$$ LANGUAGE plpgsql;
i get the following error message:
ERROR: CREATE TABLE AS is not allowed in a non-volatile function
Maybe this is not the intended usage of the stored procedures, but then is there a way for store the result of a query inside of the stored procedure for later usage in the same stored procedure, maybe like a handle or somethings?
The documentation states clearly: A stable function cannot modify the database. A temporary table is a part of a database as well, so you cannot create it, insert into, delete from etc. Your concept seems a bit strange but I don't want to judge it. There is a trick that allows what you want to do. Perfom all actions on the temp table using other functions that do not have to be stable. Example:
create or replace function create_my_temp_table()
returns void language plpgsql volatile as $$
begin
create temp table temp_table(id int);
insert into temp_table values (123);
end $$;
create or replace function stable_function()
returns text language plpgsql stable as $$
begin
perform create_my_temp_table();
return 'ok';
end $$;
Test:
select stable_function();
stable_function
-------------
ok
(1 row)
select * from temp_table;
id
-----
123
(1 row)

PL/pgSQL function name resolution with nested functions

I have two similar schemas in a single database with the same function names.
Each schema is owned by a role that matches the schema name.
I have issues about function name resolution with nested functions.
I was expecting that the outer function would call inner functions within the same schema, but it does not!
The name is resolved dynamically based on the search_path at run time which make some sens, but not as I would.
Here is a test case. Let say for example that the schemas and roles are named test and prod as follow.
Test schema:
CREATE ROLE test NOLOGIN;
CREATE SCHEMA test AUTHORIZATION test;
CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'test function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.inner_func() OWNER TO test;
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.outer_func() OWNER TO test;
Prod schema:
CREATE ROLE prod NOLOGIN;
CREATE SCHEMA prod AUTHORIZATION prod;
CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'prod function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.inner_func() OWNER TO prod;
CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.outer_func() OWNER TO prod;
Test cases:
SET search_path=test,public;
SELECT outer_func();
> test function
SELECT prod.outer_func();
> test function <<<---- was expecting prod function
SET search_path=prod,public;
SELECT prod.outer_func();
> prod function
The test shows that function names are resolved dynamically based on the search_path at run time. Is there a way to bind inner function within the scope of a schema?
I can get such a behavior by using SECURITY DEFINER functions with dynamic SQL and CURRENT_USER, but I am looking for something more straightforward.
The clean solution is to either schema-qualify the function:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT test.inner_func();
END
$func$ LANGUAGE plpgsql; -- no quotes!
Or you explicitly set the search_path per function. You can set configuration parameters this way:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT inner_func();
END
$func$ LANGUAGE plpgsql SET search_path = test, pg_temp;
Customize the search_path to your needs, possibly add public to the list. I put pg_temp at the end, so objects in the temporary schema cannot hide persisted objects. (But that's not applicable for functions.) Similar to what's explained in the manual for SECURITY DEFINER functions.
I would not advise to rely on the user setting the proper search_path. That would only make sense for "public" functions, it wouldn't be consistent with your design. Why create separate functions and then still have to rely on user settings after all? You could have a single function in the public schema to begin with, but I would not got that route in any case. Very confusing and error prone.
Also, PL/pgSQL executes statements like prepared statements internally. every time you change the search_path, all "prepared" statements from plpgsql functions have to be de-allocated, which is not helping to optimize performance.
Actually, your test case in the question only works if you set the search_path first:
SET search_path=test,public;
Else you get an error when trying to create
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
...
ERROR: function inner_func() does not exist
Syntax checks are run against the current search_path at creation time - unless you provide the search_path as suggested. That was fixed 2010 after I reported a bug.
Details for search_path:
How does the search_path influence identifier resolution and the "current schema"
How can I fake inet_client_addr() for unit tests in PostgreSQL?
And don't quote the language name. It's an identifier.