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.
Related
I have the following function in my postgres database:
create function my_schema.create_my_book(book my_schema.book) returns my_schema.book as $$
declare
v_book my_schema.book;
begin
insert into my_schema.book(title, language) values (book.title, book.language) returning * into v_book;
return v_book;
end;
$$ language plpgsql volatile;
This way I have to type out all the column names (title, language) and values (book.title, book.language). My book table is quite big so this will blow up my code by a lot, and once I add a column I will have to remember to add it to this function too.
Is there a way to directly insert the whole book my_schema.book object?
Yes, here it is. You do not even need plpgsql to do this, plain sql will do (and works faster).
create or replace function my_schema.create_my_book(arg_book my_schema.book)
returns my_schema.book as
$$
insert into my_schema.book select arg_book.* returning *;
$$ language sql volatile;
I changed the argument's name to arg_book in order to avoid possible ambiguity. And since the type of arg_book is my_schema.book this simple code adapts itself to table mutation and continues to work.
To solve the id issue
create or replace function my_schema.create_my_book(arg_book my_schema.book)
returns my_schema.book as
$$
declare
v_book my_schema.book%rowtype;
begin
arg_book.id := nextval('the-id-sequence-name');
insert into my_schema.book select arg_book.* returning * into v_book;
return v_book;
end;
$$ language plpgsql volatile;
which is pretty close to your initial function, just dynamic.
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.
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.
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; $$;
Let's say I have a function show_files(IN file text, IN suffix text, OUT statement text). In next step the function is called:
SELECT * FROM show_files(file := 'example', suffix := '.png');
My question is: Is there any solution that I could get statement that has called this function from inside that function?
I mean, after running the SELECT the output of function (OUT statement text) should be: 'SELECT * FROM show_files(file := 'example', suffix := '.png');', or is it possible to assign this statement to the variable inside the function?
I need the functionality like those with TG_NAME, TG_OP, etc. in trigger procedures.
Maybe is it possible to retrieve this statement from SELECT current_query FROM pg_stat_activity ?
When I'm trying to use it inside a function I've got an empty record:
CREATE OR REPLACE FUNCTION f_snitch(text)
RETURNS text AS
$BODY$
declare
rr text;
BEGIN
RAISE NOTICE '.. from f_snitch.';
-- do stuff
SELECT current_query into rr FROM pg_stat_activity
WHERE current_query ilike 'f_snitch';
RETURN rr;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Any help and suggestions would be happily welcome!
TG_NAME and friends are special variables that only exist for trigger functions. Regular plpgsql functions don't have anything like that. I am fresh out of ideas how you could possibly get this inside the called function in plpgsql.
You could add RAISE NOTICE to your function so you get the desired information
CREATE OR REPLACE FUNCTION f_snitch(text)
RETURNS text LANGUAGE plpgsql AS
$func$
BEGIN
RAISE NOTICE '.. from f_snitch.';
-- do stuff
RETURN 'Snitch says hi!';
END
$func$;
Call:
SELECT f_snitch('foo')
In addition to the result, this returns a notice:
NOTICE: .. from f_snitch.
Fails to please in two respects:
Calling statement is not in the notice.
No CONTEXT in the notice.
For 1. you can use RAISE LOG instead (or set your cluster up to log NOTICES, too - which I usually don't, too verbose for me). With standard settings, you get an additional line with the STATEMENT in the database log:
LOG: .. from f_snitch.
STATEMENT: SELECT f_snitch('foo')
For 2., have a look at this related question at dba.SE. CONTEXT would look like:
CONTEXT: SQL statement "SELECT f_raise('LOG', 'My message')"
PL/pgSQL function "f_snitch" line 5 at PERFORM
Ok, I've got it!
CREATE OR REPLACE FUNCTION f_snitch(text)
RETURNS setof record AS
$BODY$
BEGIN
RETURN QUERY
SELECT current_query
FROM pg_stat_activity
<strike>ORDER BY length(current_query) DESC LIMIT 1;</strike>
where current_query ilike 'select * from f_snitch%';
-- much more reliable solution
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
select * from f_snitch('koper') AS (tt text);
And here is the result:
It's probably not 100% reliable solution but for small systems (for few users) it's quite ok.