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:
Related
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!
I need to be able to get the value stored inside rec_key.empname when I call this function:
CREATE OR REPLACE FUNCTION public.txt(text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
declare
var_param text;
var_req TEXT;
rec_key record;
cur_key CURSOR FOR Select empname::varchar from employee;
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req :=
'
' || $1 || '
';
return query execute var_req;
end loop;
close cur_key;
END
$function$
;
What do I have to change to get the desired empname when calling the function?
If I call it like this it doesn't work: :(
select * from public.txt('select empid, age::integer,''''''|rec_key.empname|''''''::varchar from employee') as (empid integer, age integer, empname varchar)
To address the question asked:
CREATE OR REPLACE FUNCTION public.txt(_sql text)
RETURNS SETOF record
LANGUAGE plpgsql AS
$func$
DECLARE
_rec record;
BEGIN
FOR _rec IN
SELECT empname::text FROM employee
LOOP
RETURN QUERY EXECUTE _sql
USING _rec.empname;
END LOOP;
END
$func$;
Call:
SELECT * FROM public.txt('SELECT empid, age::integer, $1 AS empname FROM employee')
AS (empid integer, age integer, empname varchar);
The example does not make any sense, though, and all of it could be replaced with a simple query. See my anser to your earlier question:
Doesn't find variable when passing query as parameter
Use the much simpler implicit cursor of a FOR loop. See:
Cursor based records in PostgreSQL
Truncating all tables in a Postgres database
Pass the variable as value with a USING clause. $1 is the symbol to reference the first USING argument. See:
Replace double quotes with single quotes in Postgres (plpgsql)
I have a sample stored procedure where in I have to use a table for multiple operations. I want to declare the table name as a constant and then re-use it wherever required. Below is the sample code which i wrote:
CREATE OR REPLACE FUNCTION get_data()
RETURNS void AS
$func$
DECLARE
table_name_a CONSTANT TEXT = asp.monitoring_bookmark_original;
cursor_file CURSOR FOR
select distinct filename,systemuid from table_name_a;
cursor_data CURSOR FOR
select * from table_name_a where filename = v_filename and systemuid=v_systemuid order by mindatetime, maxdatetime;
BEGIN
--open the file cursor
//logic goes here
END;
$func$
LANGUAGE plpgsql;
When I try to run this procedure I am getting error:
ERROR: missing FROM-clause entry for table "asp"
LINE 1: SELECT asp.monitoring_bookmark_original
What is wrong in this code? How do I correct this?
Well you can use dynamic SQL, but realize dynamic SQL often adds way more complexity. Good when really needed but should be avoided when possible. The following shows what would be needed for what you want to do. Is not having to type the table name for each SQL statement worth the additional trouble?
create or replace function get_data()
returns void as
$func$
declare
table_name_a constant text = 'asp.monitoring_bookmark_original';
file_cursor text = 'select distinct filename,systemuid from %i';
file_ref refcursor;
file_rec record;
data_cursor text =$stmt$select * from %i where filename = '%s' and systemuid= '%s' order by mindatetime, maxdatetime$stmt$;
data_ref refcursor;
data_rec record;
begin
--open the file cursor
open file_ref for execute format(file_cursor,table_name_a);
loop
fetch next from file_ref into file_rec;
exit when not found;
-- and extending from what the second query inplies
open data_ref for execute format(data_cursor,table_name_a,file_rec.filename,file_rec.systemid);
loop
fetch next from data_ref into data_rec;
exit when not found;
--//logic goes here
end loop;
end loop ;
end;
$func$
language plpgsql;
I have this code:
DO $$
DECLARE
NODE_ID bigint := 46;
BEGIN
CREATE OR REPLACE FUNCTION funk(VAL bigint)
RETURNS bigint AS $f$
BEGIN
RETURN VAL;
END; $f$ LANGUAGE plpgsql;
RAISE NOTICE '%', funk(NODE_ID);
END $$;
I works as expected and prints 46 to the console.
I want to get rid of the parameters, because the variable is global. But I am getting errors:
DO $$
DECLARE
NODE_ID bigint := 46;
BEGIN
CREATE OR REPLACE FUNCTION funk()
RETURNS bigint AS $f$
BEGIN
RETURN NODE_ID;
END; $f$ LANGUAGE plpgsql;
RAISE NOTICE '%', funk();
END $$;
I'm getting "NODE_ID not exist". Is there a way to access the outer variable in the function?
No, that won't work, because the function has no connection to your DO block whatsoever. It is a persistent database object that will continue to exist in the database after the DO block has finished.
In essence, a function is just a string with the function body (and some metadata, see pg_proc); in this case, the function body consists of the text between the opening and the closing $f$. It is interpreted by the language handler when the function is run.
The only database data you can reference in a function are other persistent database objects, and a variable in a DO block isn't one of those.
There are no global variables in PostgreSQL except for – in a way – the configuration parameters. You can access these with the SET and SHOW SQL commands and, more conveniently in code, with the set_config and current_setting functions.
Or use dynamic SQL:
DO $$
DECLARE
NODE_ID bigint := 46;
src text := format('
CREATE OR REPLACE FUNCTION funk()
RETURNS bigint AS $f$
BEGIN
RETURN %s;
END;
$f$ LANGUAGE plpgsql;
', NODE_ID::text);
BEGIN
execute src;
RAISE NOTICE '%', funk();
END $$;
(works for me, landing on your question searching for solution of same problem)
I am call the function but it is returning error that array value must start with "{" or dimension information using
Create or Replace Function get_post_process_info(IN v_esdt_pp character varying[])
Returns setof Record as
$$
Declare
post_processes RECORD;
esdt_value character varying;
v_sdsname character varying[];
v_dimension character varying[];
counter int := 1;
Begin
-- to loop through the array and get the values for the esdt_values
FOR esdt_value IN select * from unnest(v_esdt_pp)
LOOP
-- esdt_values as a key for the multi-dimensional arrays and also as the where clause value
SELECT distinct on ("SdsName") "SdsName" into v_sdsname from "Collection_ESDT_SDS_Def" where "ESDT" = esdt_values;
raise notice'esdt_value: %',esdt_value;
END LOOP;
Return ;
End
$$ Language plpgsql;
Select get_post_process_info(array['ab','bc]);
Your function sanitized:
CREATE OR REPLACE FUNCTION get_post_process_info(v_esdt_pp text[])
RETURNS SETOF record AS
$func$
DECLARE
esdt_value text;
v_sdsname text[];
v_dimension text[];
counter int := 1;
BEGIN
FOR esdt_value IN
SELECT * FROM unnest(v_esdt_pp) t
LOOP
SELECT distinct "SdsName" INTO v_sdsname
FROM "Collection_ESDT_SDS_Def"
WHERE "ESDT" = esdt_value;
RAISE NOTICE 'esdt_value: %', esdt_value;
END LOOP;
END
$func$ Language plpgsql;
Call:
Select get_post_process_info('{ab,bc}'::text[]);
DISTINCT instead of DISTINCT ON, missing table alias, formatting, some cruft, ...
Finally the immediate cause of the error: a missing quote in the call.
The whole shebang can possibly be replaced with a single SQL statement.
But, obviously, your function is incomplete. Nothing is returned yet. Information is missing.