Return SYS_REFCURSOR From Procedure - Oracle to PostgreSQL Migration

An Oracle stored procedure can return a cursor to the caller, for example:

Oracle:

  -- Get list of employees for the specified department
  CREATE OR REPLACE PROCEDURE getEmployeesByDept (
     p_deptno IN emp.deptno%TYPE, 
     p_recordset OUT SYS_REFCURSOR
  ) AS 
  BEGIN 
    OPEN p_recordset FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno
      ORDER BY ename;
  END getEmployeesByDept;
  /

Then you can call the procedure and fetch records from the returned cursor as follows:

Oracle:

  SET SERVEROUTPUT ON
 
  DECLARE
    cur SYS_REFCURSOR;
    empno emp.empno%TYPE;
    ename emp.ename%TYPE;
  BEGIN
    -- Get employees list
    getEmployeesByDept(p_deptno => 30, p_recordset => cur);
 
    -- Output the records  
    LOOP 
      FETCH cur INTO empno, ename;
      EXIT WHEN cur%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE(empno || ', ' || ename);
    END LOOP;
    CLOSE cur;
  END;
  /

Sample output:

7499, ALLEN
7698, BLAKE
7900, JAMES
7654, MARTIN
7844, TURNER
7521, WARD

PostgreSQL also supports REFCURSOR but it is an IN parameter containing the cursor name, and the actual REFCURSOR value is returned using the RETURN statement:

PostgreSQL:

  -- Get list of employees for the specified department
  CREATE OR REPLACE FUNCTION getEmployeesByDept (
     p_deptno IN emp.deptno%TYPE, 
     p_recordset REFCURSOR
  ) RETURNS REFCURSOR AS $$ 
  BEGIN 
    OPEN p_recordset FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno
      ORDER BY ename;
 
      RETURN p_recordset;
  END;
  $$ LANGUAGE plpgsql;

Note that the PROCEDURE was converted to FUNCTION, RETURNS REFCURSOR clause and RETURN statements were added. Now you can call this function from a PL/pgSQL block as follows:

PostgreSQL:

  DO $$
  DECLARE
    cur REFCURSOR = 'cur';
    empno emp.empno%TYPE;
    ename emp.ename%TYPE;
  BEGIN
    -- Get employees list
    PERFORM getEmployeesByDept(p_deptno := 30, p_recordset := 'cur');
 
    -- Output the records  
    LOOP 
      FETCH cur INTO empno, ename;
      EXIT WHEN NOT FOUND;
      RAISE NOTICE '%',empno || ', ' || ename;
    END LOOP;
    CLOSE cur;
  END;
  $$;

Sample output:

NOTICE: 7499, ALLEN
NOTICE: 7698, BLAKE
NOTICE: 7900, JAMES
NOTICE: 7654, MARTIN
NOTICE: 7844, TURNER
NOTICE: 7521, WARD

Returning Multiple Cursors from Stored Procedure

Oracle allows you to return multiple SYS_REFCURSORs from a stored procedures, for example:

Oracle:

-- Get list of employees for the specified department
  CREATE OR REPLACE PROCEDURE getEmployeesByDept2 (
     p_deptno IN emp.deptno%TYPE, 
     p_recordset OUT SYS_REFCURSOR,
     p_recordset2 OUT SYS_REFCURSOR
  ) AS 
  BEGIN 
    OPEN p_recordset FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno
      ORDER BY ename;
 
    OPEN p_recordset2 FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno;
 
  END getEmployeesByDept2;
  /

PostgreSQL also allows you to return multiple REFCURSORs from a stored procedure, for example:

PostgreSQL:

  CREATE OR REPLACE FUNCTION getEmployeesByDept3 (
     p_deptno IN emp.deptno%TYPE, 
     p_recordset REFCURSOR,
     p_recordset2 REFCURSOR
  ) RETURNS SETOF REFCURSOR AS $$ 
  BEGIN 
    OPEN p_recordset FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno
      ORDER BY ename;
    RETURN NEXT p_recordset;
 
    OPEN p_recordset2 FOR
      SELECT empno, ename
      FROM  emp
      WHERE deptno = p_deptno;
    RETURN NEXT p_recordset2;  
 
  END;
  $$ LANGUAGE plpgsql;

Note that you have to specify SETOF in the RETURNS clause, and use RETURN NEXT to return every cursor.

For more information, see Oracle to PostgreSQL Migration.