PostgreSQL functions select* - postgresql

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

Related

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 handle refcursor not retrieving data in PGSQL

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;
$$;

Declare type of setof bigint in plpgsql function and assign into from select union

The following code currently works on PostgreSQL 13.3 (via Supabase.io). Both functions get_owned_base_ids and get_bases_editable have return type of setof bigint:
CREATE FUNCTION get_owned_base_ids()
returns setof bigint
stable
language sql
as $$
select id
from bases
where bases.owner_user_id = auth.uid();
$$;
-- CREATE FUNCTION get_bases_editable()
-- returns setof bigint
-- ... similar to get_owned_base_ids()
-- $$;
CREATE FUNCTION xyz (base_id bigint)
returns int
language plpgsql
as $$
BEGIN
IF base_id not in (select get_owned_base_ids() UNION select get_bases_editable()) THEN
-- note: actual function logic is simplified for this question
return 1;
END IF;
return 0;
END;
$$;
Is it possible to define a setof bigint and assign that from the select union? Something like this:
CREATE FUNCTION xyz (base_id bigint)
returns int
language plpgsql
as $$
DECLARE
allowed_base_ids bigint; -- needs to be a setof
BEGIN
select into allowed_base_ids get_owned_base_ids() UNION select get_bases_editable();
IF kv.base_id not in allowed_base_ids THEN
-- note: actual function logic is simplified for this question
return 1;
END IF;
return 0;
END;
$$;
It usually does not make much sense and use much memory of the result set is large, but you can use an array:
DECLARE
allowed_base_ids bigint[];
BEGIN
allowed_base_ids := array(SELECT * FROM get_owned_base_ids()
UNION ALL
SELECT * FROM get_bases_editable());
IF kv.base_id <> ALL (allowed_base_ids) THEN
...
END IF;
END;

postgresql: converting CTE column to array

I am trying to convert one of the columns of CTE to array. I keep on getting "syntax error at or near" followed by "ret := array(".
My objective is that the table I am returning from a_function() in example below is stored as a variable to be referred later in function. But I could not find a syntax to do so. So, instead of using CTE, if I can use something else, that would work just as nicely.
Note: I am trying this in pgAdmin III.
create or replace function a_function()
--returns int[][] as
returns table(column1 int, column2 int) as
$body$
begin
return query
select 1,2;
end;
$body$
language 'plpgsql'
;
--select * from a_function();
create or replace function test_a_function()
returns void as
$body$
declare ret int[];
begin
with ret_cte(column1, column2) as (
select * from a_function()
)
ret := array(
select column1 from ret_cte
)
;
--raise notice '%', array_to_string(ret, ',');
end;
$body$
language 'plpgsql'
;
--select test_a_function();
I am trying to convert one of the columns of CTE to array. I keep on getting "syntax error at or near" followed by "ret := array("
You could use:
ret :=array(with ret_cte(column1, column2) as (
select * from a_function()
)
select column1 from ret_cte
);
Rextester Demo
Arrays can be built and returned using the array_agg() function. For example, your second function could be written using a SQL language function that returns the array as follows:
create or replace function test_a_function()
returns int[] as
$body$
select array_agg(column1) from a_function();
$body$
language 'sql';
Alternatively, you can assign the array to a variable like this:
create or replace function test_a_function()
returns void as
$body$
declare
ret int[];
begin
select array_agg(column1)
into ret
from a_function();
raise info '%', ret;
end;
$body$
language 'plpgsql';