How to use the defined the cursor returned from other function - postgresql

I have a simple function that returns a cursor to the function caller.
create or replace function func_get_cursor()
returns refcursor as $$
declare
my_cursor refcursor:='hello_cursor';
r record;
begin
open my_cursor for select empno,ename, job from emp;
return my_cursor;
end;
$$ language plpgsql;
Then I define another function that want to use the above defined cursor:
create or replace function func_use_cursor()
returns setof record as $$
declare
my_cursor refcursor;
begin
select func_get_cursor() into my_cursor;
fetch 4 from my_cursor;
commit;
-- how to collect the result rows and return
return;
end;
$$ language plpgsql;
There are 2 problems here:
it complains that there are errors for the ;around fetch 4 from my_cursor;,but i don't find out where the problem is.
I want to fetch 4 rows from the cursor, and return the 4 rows(the return type is setof record), I would ask how to collect the result rows and return.
Thanks!

Related

How many rows will be fetched from cursor with my code

I am new to plpgsql, and I am excercising cursor.
I have following simple code,
create or replace function func_cursor_2()
returns setof numeric as $$
declare
cursor1 CURSOR for select empno,ename, job from emp;
r record;
begin
open cursor1;
loop
fetch from cursor1 into r;
exit when not found;
return next r.empno;
end loop;
close cursor1;
end;
$$ language plpgsql;
select func_cursor_2()
With fetch from cursor1 into r,
It looks to me that I am fetching the result rows one by one?
Is there way to specify 100 rows for one fetch from cursor?
Why bother with a cursor at all. This can be done in 1 statement.
create or replace function func_cursor_2()
returns setof numeric
language sql
as $$
select empno
from emp
limit 100;
$$;
However, the above will not return consistent results. To generate consistent you will need to add order by empno and perhaps offset depending on your exact needs.
Note: Not Tested.

Fetch stored procedure refcursor output and insert into temp table

I have a stored procedure(P1) that returns a refcursor and few other text datatype values.
I have another procedure(P2) where I need to the fetch P1's refcursor output and insert it into a temporary table. The temporary table has matching columns and datatypes.
create or replace procedure P1(inout rfcur refcursor, in dtl text)
as
$$
begin
open rfcur for select * from tst_dump where ident = dtl;
end;
$$
language plpgsql;
create or replace P2(inout rfc refcursor, in dt_array text[])
as
$$
declare
i record;
cur refcursor;
begin
for i in array_lower(dt_array, 1)..array_upper(dt_array, 1) loop
call P1(cur, i);
--I need to fetch the result set from `cur` and store into a temp table `t_tab1`.
end loop;
end;
$$
language plpgsql;
Is it possible to achieve this in Postgres?
NOTE: I'm not supposed to make any changes to the procedure P1.
p2 could look like this:
CREATE PROCEDURE p2(IN dt_array text[])
LANGUAGE plpgsql AS
$$DECLARE
r record;
i integer;
cur refcursor;
BEGIN
FOR i IN array_lower(dt_array, 1)..array_upper(dt_array, 1) LOOP
CALL p1(cur, i::text);
LOOP
FETCH cur INTO r;
EXIT WHEN NOT FOUND;
INSERT INTO t_tab1 (...) VALUES (r.col1, r.col2, ...;
END LOOP;
END LOOP;
END;$$;
You should indent your code. That's the basic requirement when you program.
It seems to me that you are doing something the wrong way. Using procedures and cursors complicates everything and makes it slower.
You should do something like
INSERT INTO t_tab
SELECT /* your original query */;

Postgresql - Using multi result sets in another function

I follow this tutorial in order create a function
http://www.sqlines.com/postgresql/how-to/return_result_set_from_stored_procedure
Here is the code
CREATE FUNCTION func1(vtoken character varying, ref1 refcursor, ref2 refcursor)
RETURNS SETOF refcursor AS $BODY$
DECLARE
rec record;
BEGIN
OPEN ref1 FOR
SELECT * FROM table1;
RETURN NEXT ref1;
OPEN ref2 FOR
SELECT * FROM table2;
RETURN NEXT ref2;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;
I want to create another function and call func1.
CREATE OR REPLACE FUNCTION script(vcodebar character varying)
RETURNS void AS
$BODY$
DECLARE
BEGIN
SELECT func1(vtoken,'details', 'amount');
FETCH ALL IN "details";
END;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
However I receive an error:
FETCH ALL IN "details";
I can not add BEGIN ... COMMIT before call func1, because it's wrapped in a block BEGIN ... END;
How can I use 2 refcursors from the func1 ?
The cursors returned from the function are closed as soon as the call to the function completes. You should wrap the block from the SELECT statement until after the last use of the cursors in a transaction block:
BEGIN
SELECT issue_ticket(vtoken, 'details', 'amount');
FETCH ALL IN "details";
-- More statements
COMMIT; -- or ROLLBACK

How to return no records found from a stored procedure

Is it possible to have a stored procedure behave exactly like a regular select query when no records are found, or is this a driver issue.
For example, with go, a query that returns no rows will return an sql.ErrNoRows error. However, this will not:
create table emptytable(id int);
create function selectany() returns emptytable as $$
DECLARE
_out emptytable;
BEGIN
SELECT * INTO emptytable FROM emptytable limit 1;
RETURN _out;
END;
$$ LANGUAGE PLPGSQL;
I have tried SELECT INTO STRICT, and while that raises a "query returned no rows" error, it is not the same as a non-stored procedure query. Neither is raising NO_DATA_FOUND.
If I understand your requirements correctly:
Return one or no row from a function and allow to do more with the returned row (if any).
Test table:
CREATE TABLE emptytable(id int, txt text); -- multiple columns
To return one or no complete table row:
CREATE OR REPLACE FUNCTION selectany_all()
RETURNS SETOF emptytable AS
$func$
DECLARE
_out emptytable;
BEGIN
FOR _out IN
SELECT * FROM emptytable LIMIT 1
LOOP
-- do something with _out before returning
RAISE NOTICE 'before: %', _out;
RETURN NEXT _out;
-- or do something with _out after returning row
RAISE NOTICE 'after: %', _out;
END LOOP;
END
$func$ LANGUAGE plpgsql;
For a more flexible approach: return arbitrary columns:
CREATE OR REPLACE FUNCTION selectany_any()
RETURNS TABLE (id int, txt text) AS
$func$
BEGIN
FOR id, txt IN
SELECT e.id, e.txt FROM emptytable e LIMIT 1
LOOP
-- do something with id and text before returning
RAISE NOTICE 'before: %, %', id, txt;
RETURN NEXT;
-- or do something with id and text after returning row
RAISE NOTICE 'after: %, %', id, txt;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Note, the LOOP is never entered if there is no row. Accordingly you will get no NOTICE from my test code.
Both functions work for n rows returned as well, LIMIT 1 is just for this particular request.
Closely related, wtih more explanation:
Return multiple fields as a record in PostgreSQL with PL/pgSQL
2.5 options:
1a) If you just need to return a query, you can use SETOF and RETURN QUERY
1b) or just use language SQL as #ClodoaldoNeto, which returns a query natively using sql's SELECT stmt
2) If you need to process the result in the procedure, you must use SETOF and RETURN NEXT, ensuring you check IF FOUND THEN RETURN; (note lack of NEXT, which if given will act as a single blank row is returned)
Ideally, I'd like to not use SETOF for procedures known to return exactly none or 1 rows, but it seems SETOF is required to get a procedure to query like an sql statement from the app and have drivers recognize NO ROWS RETURNED
Examples below:
create table emptytable(id int);
create function selectany() returns setof emptytable as $$
DECLARE
_out emptytable;
BEGIN
SELECT * INTO _out FROM emptytable limit 1;
IF FOUND THEN
RETURN _out;
END IF;
RETURN;
END;
$$ LANGUAGE PLPGSQL;
create function selectany_rq() returns setof emptytable as $$
BEGIN
RETURN QUERY SELECT * INTO _out FROM emptytable limit 1;
END;
$$ LANGUAGE PLPGSQL;
As suggested in the comments do return setof emptytable
create function selectany()
returns setof emptytable as $$
select *
from emptytable
limit 1
;
$$ language sql;
Plain sql can do that

Parameterize table name for cursor bound variable

Below is the function where the records are stored in a record variable for each iteration. Here the table name is hardcoded for cursor bound variable. Is there is any way I can pass the table name as a parameter through this function?
CREATE OR REPLACE FUNCTION test1()
RETURNS SETOF refcursor AS
$BODY$
DECLARE
curs2 CURSOR FOR SELECT * FROM datas.test1000;
begin
FOR recordvar IN curs2 LOOP
RAISE NOTICE 'recordvar: %',recordvar;
END LOOP ;
end;
$BODY$
language plpgsql;
No, not for a bound cursor.
But you can easily pass a name for opening an unbound cursor. There is an example in the manual doing precisely that.
Your function could look like this:
CREATE OR REPLACE FUNCTION test2(_tbl regclass)
RETURNS void AS
$func$
DECLARE
_curs refcursor;
rec record;
BEGIN
OPEN _curs FOR EXECUTE
'SELECT * FROM ' || _tbl;
LOOP
FETCH NEXT FROM _curs INTO rec;
EXIT WHEN rec IS NULL;
RAISE NOTICE 'rec: %', rec;
END LOOP;
END
$func$ language plpgsql;
The special FOR loop can only be used with bound cursors. I supplied an alternative.
More explanation in this closely related answer:
Update record of a cursor where the table name is a parameter
I use the object identifier type regclass to pass the table name to avoid SQL injection.
More about that in this related answer on dba.SE: