Explain - inserts only one row - sql

I'm trying to manually save optimizer plan for further analysis, like this:
do $$
declare
tmp text;
begin
explain
select * from public.some_table where 1=2 into tmp;
insert into public.plans(plan) values (tmp);
end; $$
But when I select it later, I see it only saved first row from the explain statement:
Result (cost=0.00..82.97 rows=1 width=114)
How can I make it to save the whole plan?

Because explain cannot be used like e.g. a SELECT this is a bit tricky and you need dynamic SQL for this.
The following worked for me:
do
$$
declare
plan_line record;
begin
for plan_line in execute 'explain select * from public.some_table where 1=2' loop
insert into plans values (plan_line."QUERY PLAN");
end loop;
end;
$$
Having the statement to be explained in a string makes things a bit more complicated.
If I needed that on a regular basis, I would probably create a function that does this:
create or replace function explain(to_explain text)
returns setof text
as
$$
declare
plan_line record;
begin
for plan_line in execute 'explain '||to_explain loop
return next plan_line."QUERY PLAN";
end loop;
end;
$$
language plpgsql;
Then you can do something like:
insert into plans
select *
from explain('select ...');

Related

How do I execute a SELECT query in a text string?

I have a function my_funct(param1,param2) returning a SELECT QUERY as a text, I usually C/c the result, remove quotes, then run it.
How can execute the returned text in a query that call the function ?
I expect something like this:
SELECT resultOf( myfunct('foo','bar'))
with no extra columnname/type to declare. If such function does not exits built-in, let's create it, I don't mind.
If I understood correctly you want
Call a function returning a select query as text
Run that query to get the result.
Number and type of columns are dynamic
1. Using Do Block:
DO
$$
declare
ref_cursor refcursor:='mycursor';
begin
open ref_cursor for execute (select * from my_funct('foo','bar')) ;
end;
$$;
--Fetch all data from cursor
Fetch all from mycursor;
2. Using Function returning refcursor:
You can create a function like below:
create function my_funct1(param1 text) returns refcursor as
$$
declare
ref_cursor refcursor:='mycursor';
begin
open ref_cursor for execute param1;
return ref_cursor;
end;
$$
language plpgsql
To call above function use following code:
begin ;
select my_funct1((select * from my_funct('foo','bar')) );
fetch all from mycursor;
commit;
DEMO

How to insert rows to table in a loop

I have the following plpgsql function in PostgreSQL:
CREATE OR REPLACE FUNCTION func1()
RETURNS SETOF type_a AS
$BODY$
declare
param text;
sqls varchar;
row type_a;
begin
code.....
sqls='select * from func3(' || param || ') ';
for row in execute sqls LOOP
return next row;
END LOOP;
end if;
return;
end
$BODY$
LANGUAGE plpgsql VOLATILE
I want to add an insert statment into the loop, so that the loop will work as it is now but also all rows will be saved in a table.
for row in execute sqls LOOP
INSERT INTO TABLE new_tab(id, name)
return next row;
the thing is that I don't know how to do that... the insert statment normaly has syntax of:
INSERT INTO new_tab(id, name)
SELECT x.id, x.name
FROM y
but this syntax doesn't fit here. There is no query to select rows from.... the rows are in the loop.
Basic insert with values looks like this:
INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);
Based on the additional comments you need to use cursor instead of execute sqls.
No need for a loop, you can use insert .. select ... returning in dynamic SQL just as well:
create or replace function func1()
returns table (id integer, name text)
as
$$
declare
param text;
begin
param := ... ;
return query execute
'insert into new_tab (id, name)
select id, name
from func3($1)
returning *'
using param;
end;
$$
language plpgsql;
Note that I used a parameter placeholder and the USING clause instead of concatenating the parameter into the query - much more robust.

Write like and not like dynamically in plpgsql

SQL1:
select regno from student where regno **like 'ABCD%'**
This is running successfully. But how can I write like 'ABCD%' dynamically?
For example:
CREATE OR REPLACE FUNCTION check_regno(refcursor, character varying)
RETURNS refcursor AS
$BODY$
begin
select regno from student where regno $1
return $1;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Now I want to pass $1 as like 'ABCD%' i.e.:
select check_regno(f1, "like 'ABCD%'")
This will give error at $1:
Please suggest how to achieve this.
As #Igor mentioned, this is error prone. I would go one step further: Don't do it. You invite SQL injection. Consider this related answer on dba.SE.
In fact, I don't see anything in your question warranting dynamic SQL at all. Use a plain SQL function and pass a plain string value instead:
CREATE OR REPLACE FUNCTION check_regno(_like bool, _filter text)
RETURNS SETOF text AS
$func$
SELECT regno FROM student
WHERE CASE WHEN $1 THEN regno ~~ $2 ELSE regno !~~ $2 END
$func$ LANGUAGE sql;
~~ and !~~ being Postgres operators for LIKE and NOT LIKE (you can use either).
Call:
SELECT * FROM check_regno(TRUE, 'ABCD%');
SELECT * FROM check_regno(FALSE, 'DEFG%');
Try something like:
CREATE OR REPLACE FUNCTION check_regno(p_filter varchar)
RETURNS SETOF student.regno%TYPE AS
$BODY$
begin
return query execute 'select regno from student where regno'||p_filter;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
SELECT * FROM check_regno('like ''ABCD%''');
But this type of dynamic SQL is error prone and can allow SQL injections.

Testing PostgreSQL functions that consume and return refcursor

I want to test results of a Postgres function (changing the function is not a possibility).
The function receives as arguments a REFCURSOR and several other things and returns the same RECURSOR.
get_function_that_returns_cursor(ret, 4100, 'SOMETHING', 123465)
Now I want to create a small test in Postgres to get the results of this FUNCTION.
Something Like the code below (this is my approach but it is not working):
DO $$ DECLARE
ret REFCURSOR;
row_to_read table_it_will_return%ROWTYPE ;
BEGIN
PERFORM get_function_that_returns_cursor(ret, 4100, 'SOMETHING', 123465);
-- OR SELECT get_function_that_returns_cursor(ret, 4100, 'SOMETHING', 123465) INTO ret
FOR row_to_read IN SELECT * FROM ret LOOP
-- (...)
RAISE NOTICE 'Row read...';
END LOOP;
CLOSE ret;
END $$;
Any suggestion on how to get this to work? A generic solution that can be used for testing this type of functions (that get a Cursor and return a Cursor?
And if we don't know the rowtype that is being returned how could we do it?
Q1
Your "small test" can be plain SQL:
BEGIN;
SELECT get_function_that_returns_cursor('ret', 4100, 'foo', 123); -- note: 'ret'
FETCH ALL IN ret; -- works for any rowtype
COMMIT; -- or ROLLBACK;
Execute COMMIT / ROLLBACK after you inspected the results. Most clients only display the result of the lat command.
More in the chapter Returning Cursors of the manual.
Q2
And if we don't know the rowtype that is being returned how could we do it?
Since you only want to inspect the results, you could cast the whole record to text.
This way you avoid the problem with dynamic return types for the function altogether.
Consider this demo:
CREATE TABLE a (a_id int PRIMARY KEY, a text);
INSERT INTO a VALUES (1, 'foo'), (2, 'bar');
CREATE OR REPLACE FUNCTION reffunc(INOUT ret refcursor)
LANGUAGE plpgsql AS
$func$
BEGIN
OPEN ret FOR SELECT * FROM a;
END
$func$;
CREATE OR REPLACE FUNCTION ctest()
RETURNS SETOF text
LANGUAGE plpgsql AS
$func$
DECLARE
curs1 refcursor;
rec record;
BEGIN
curs1 := reffunc('ret'); -- simple assignment
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND; -- note the placement!
RETURN NEXT rec::text;
END LOOP;
END
$func$;
db<>fiddle here
Old sqlfiddle
This worked for what I wanted:
DO $$ DECLARE
mycursor REFCURSOR;
rec RECORD;
BEGIN
SELECT 'ret' INTO mycursor FROM get_function_that_returns_cursor('ret'::REFCURSOR, 4100, 'SOMETHING', 123465);
WHILE (FOUND) LOOP
FETCH mycursor INTO rec;
RAISE NOTICE 'Row read. Data: % ', rec.collumn_name;
END LOOP;
END $$

Calling a function that returns a refcursor

I am using Postgresql 8.3 and have the following simple function that will return a refcursor to the client
CREATE OR REPLACE FUNCTION function_1() RETURNS refcursor AS $$
DECLARE
ref_cursor REFCURSOR;
BEGIN
OPEN ref_cursor FOR SELECT * FROM some_table;
RETURN (ref_cursor);
END;
$$ LANGUAGE plpgsql;
Now , I can use the following SQL commands to call this function and manipulate the returned cursor ,but the cursor name is automatically generated by the PostgreSQL
BEGIN;
SELECT function_1(); --It will output the generated cursor name , for example , "<unnamed portal 11>" ;
FETCH 4 from "<unnamed portal 11>";
COMMIT;
Besides explicitly declaring the cursor name as the input parameter of the function as described by 38.7.3.5. Returning Cursors, can I declare my own cursor name and use this cursor name to manipulate the returned cursor instead of Postgresql automatically generates for me ?
If not, are there any commands that can get the generated cursor name ?
I'm not quite sure from wich version of Postgre this is available (in 8.4 it is valid) but i found quite easiest to define the cursor name when you declare it, like this:
CREATE OR REPLACE FUNCTION function_1() RETURNS refcursor AS $$
DECLARE
ref_cursor REFCURSOR := 'mycursor';
BEGIN
OPEN ref_cursor FOR SELECT * FROM some_table;
RETURN (ref_cursor);
END;
$$ LANGUAGE plpgsql;
And then you can get it like this:
BEGIN;
SELECT function_1();
FETCH 4 from mycursor;
COMMIT;
I find this method less cumbersome.
Hope that helps.
Yes, use:
CREATE OR REPLACE FUNCTION function_1(refcursor) RETURNS refcursor AS $$
BEGIN
OPEN $1 FOR SELECT * FROM some_table;
RETURN $1;
END;
$$ LANGUAGE plpgsql;
Result:
SELECT function_1('myowncursorname');
function_1
-----------------
myowncursorname
(1 row)
It looks like auto-generated name is <unnamed portal n>, where n is natural number (from 1).
EDIT:
As another way you could use pg_cursors view with such query to obtain generated cursor name:
SELECT name FROM pg_cursors WHERE statement LIKE 'SELECT * FROM some_table';
For example:
BEGIN;
SELECT function_1();
SELECT name FROM pg_cursors WHERE statement LIKE 'SELECT * FROM some_table';
COMMIT;
Result:
function_1
--------------------
<unnamed portal 3>
(1 row)
name
--------------------
<unnamed portal 3>
(1 row)