Search code examples
postgresqldynamicplpgsqldatabase-cursor

Dynamically generated CURSOR in Postgresql


I have got a cursor, it is pointing to a SELECT, but this select is generated dynamically. I want to assign the statement after the declarement. I have done an example working and another example NOT working. This is a simple example to print some data only. This is the table:

CREATE TABLE public.my_columns (
  id      serial NOT NULL,
  "name"  varchar(30) NOT NULL,
  /* Keys */
  CONSTRAINT my_columns_pkey
    PRIMARY KEY (id)
) WITH (
    OIDS = FALSE
  );

CREATE INDEX my_columns_index01
  ON public.my_columns
  ("name");


INSERT INTO public.my_columns
    ("name")
VALUES
    ('name1'),
    ('name2'),
    ('name3'),
    ('name4'),
    ('name5'),
    ('name6');

This is the function(I have put the working code and the code not working):

CREATE OR REPLACE FUNCTION public.dynamic_table
(
)
RETURNS text AS $$
DECLARE
       v_sql_dynamic varchar;

       --NOT WORKING:
       --db_c CURSOR IS (v_sql_dynamic::varchar);

       --WORKING:
       db_c CURSOR IS (SELECT id, name from public.my_columns);

       db_rec RECORD;

BEGIN
     v_sql_dynamic := 'SELECT id, name from public.my_columns';
    FOR db_rec IN db_c LOOP

        RAISE NOTICE 'NAME: %', db_rec.name;
    END LOOP;
    RETURN 'OK';

EXCEPTION WHEN others THEN

    RETURN 'Error: ' || SQLERRM::text || ' ' || SQLSTATE::text;
END; 
$$ LANGUAGE plpgsql;

Any ideas?

Thank you.


Solution

  • Do you really need the explicit cursor? If you need iterate over dynamic SQL, then you can use FOR IN EXECUTE. It is loop over implicit (internal) cursor for dynamic SQL

    FOR db_rec IN EXECUTE v_sql_dynamic
    LOOP
      ..
    END LOOP
    

    Little bit more complex solution is described in documentation - OPEN FOR EXECUTE:

    do $$
    declare r refcursor; rec record;
    begin 
      open r for execute 'select * from pg_class'; 
      fetch next from r into rec;
      while found 
      loop
        raise notice '%', rec; 
        fetch next from r into rec; 
      end loop;
      close r; 
    end $$;
    

    With this kind of cursor, you cannot to use FOR IN