how to handle refcursor not retrieving data in PGSQL - postgresql

How to handle refcursor not retrieving data(empty resultset) in PGSQL?
create or replace function function_1( In tblname varchar(15))
returns refcursor
language plpgsql
as $$
declare
v_cnt integer;
r_ref refcursor='ref2';
v_sql text='select * from tbl where 1=2';--default statement;shld return empty if
--input is invalid
begin
if tblname ilike 'scientist' then
v_sql:='select * from tbl'; --table name 1
end if;
open r_ref for
execute v_sql;
return r_ref;
end;
$$;
Is there any other way to handle without using dynamic sql?
execution: Test case 1 : should return empty result set
select * from function_1('invalid');
fetch all in "ref2";
execution: Test case 2 : should return proper data from able
select * from function_1('scientist');
fetch all in "ref2";
Thanks

You don't need a dynamic query when you only have one test case to cover = 'scientist'. In this case, you can simply do :
create or replace function function_1( In tblname varchar(15))
returns setof scientist language plpgsql as $$
begin
if tblname ilike 'scientist' then
return query
select * from scientist ;
end if;
end;
$$;

Related

Argument not taking the value from Postgres function

I have a simple Postgres function where I want to take table_name as a parameter and pass it into an argument and delete the data from table by condition.
CREATE OR REPLACE FUNCTION cdc.audit_refresh(tablename text)
RETURNS integer AS
$$
BEGIN
delete from tablename where id<4;
RETURN(select 1);
END;
$$ LANGUAGE plpgsql;
select cdc.audit_refresh('cdc.adf_test');
But it throws out an error that tablename
ERROR: relation "tablename" does not exist in the delete statement.(refer snapshot)
What you want to achieve is to execute Dynamic SQL statements. You can do this with EXECUTE. See more here
CREATE OR REPLACE FUNCTION audit_refresh(tablename text)
RETURNS integer AS
$$
DECLARE
stmt TEXT;
BEGIN
stmt = 'delete from '||tablename||' where id<4;';
EXECUTE stmt;
RETURN 1;
END
$$ LANGUAGE plpgsql;

Select Statement using Stored Procedure

May I ask on how to call a method when the content of the stored procedure is about select statement? (Using postgreSQL)
CREATE OR REPLACE PROCEDURE select_table(table_name VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE('SELECT * FROM' || ' ' || quote_ident(table_name));
END $$;
CALL select_table('employee_table');
EDITED(USING FUNCTION)
CREATE OR REPLACE FUNCTION select_table(table_name VARCHAR(255))
language plpgsql
as $$
BEGIN
SELECT * FROM table_name
RETURN table_name;
END $$;
In PostgreSQL procedures doesn't execute any select statements and doesn't have return.
For returning data you can use functions. But functions also cannot return different structural data, examples:
CREATE OR REPLACE FUNCTION fr_test()
RETURNS TABLE(id integer, bookname character varying)
LANGUAGE plpgsql
AS $function$
begin
return QUERY
SELECT tb.id, tb.bookname from rbac.books tb;
end;
$function$
;
or
CREATE OR REPLACE FUNCTION fr_test()
RETURNS setof public.books
LANGUAGE plpgsql
AS $function$
begin
return QUERY
SELECT * from public.books;
end;
$function$
;
But for returning difference tables you can do it using procedures and using out refcursor, like as in Oracle. For example:
create or replace procedure pr_test(OUT r1 refcursor)
as $$
begin
open r1 for
select * from public.books;
end;
$$ language plpgsql;

Save execute results into a table

Below is a simplified postgres stored procedure I am trying to run:
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute(' select *
into my_schema.MyTable_'||suffix||'
From my_schema.MyTable
');
end;
$$
language plpgsql;
When I attempt to run using something like:
call my_schema.tst('test');
I get this error Invalid operation: EXECUTE of SELECT ... INTO is not supported;
Is it possible to execute a dynamic query that creates a new table? I have seen examples that look like:
Execute('... some query ...') into Table;
but for my use case I need the resulting tablename to be passed as a variable.
In PostgreSQL you can use INSERT INTO tname SELECT...
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' INSERT INTO my_schema.MyTable_'||suffix||' SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;
or Use CREATE TABLE tname AS SELECT..., :
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' CREATE TABLE my_schema.MyTable_'||suffix||' as SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;

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

PostgreSQL functions select*

How can I use:
select * from <some_table>;
in a FUNCTION in Postgres?
CREATE OR REPLACE FUNCTION my_function() RETURNS INTEGER AS '
DECLARE
your_record your_table%ROWTYPE;
BEGIN
FOR your_record IN SELECT * FROM your_table
LOOP
--
-- You can access fields of your table using .
-- your_record.your_field
...
END LOOP;
END;
' LANGUAGE 'plpgsql'
STABLE;
or
CREATE OR REPLACE FUNCTION my_function() RETURNS INTEGER AS '
DECLARE
your_record your_table%ROWTYPE;
BEGIN
SELECT * INTO your_record FROM your_table;
--
-- You can access fields of your table using .
-- your_record.your_field
END;
' LANGUAGE 'plpgsql'
STABLE;
EDIT:
With join returning a record:
CREATE OR REPLACE FUNCTION my_function() RETURNS SETOF record AS '
DECLARE
your_record record;
BEGIN
--
-- You should specify a list of fields instead of *
--
FOR your_record IN SELECT * FROM your_table INNER JOIN ...
RETURN NEXT your_record;
END LOOP;
END;
' LANGUAGE 'plpgsql'
STABLE;
To use my_function(), you have to specify fields and datatypes:
See details here
There is a smipler method, if you want to use SQL in the function, use SQL language in the function:
CREATE FUNCTION getallzipcodes()
RETURNS SETOF zip AS
$BODY$
SELECT * FROM zip;
$BODY$
LANGUAGE 'sql';
And this might be useful too (how to call the function)
SELECT function_returning_setof(); -- Wrong!
SELECT * FROM function_returning_setof(); -- OK!
SELECT function_returning_scalar(); -- OK
reference