Reputation: 23
I have some procedure which returns REF CURSOR(I can't change it or avoid using it), but contents may be different (number of columns) depending on an IN variable. Passing one value we'll get two columns, and passing another value we'll get three columns. I need to get results from that cursor using a plain SELECT statement so I ended up with PIPLINE FUNCTION. But I can't find a solution for dynamically changing set of columns got from cursor. Is there any other way to "select" from cursor or correctly handle dynamically changing set of columns? Thanks a lot!
CREATE OR REPLACE package MYPKG as
TYPE REC IS RECORD (
A NUMBER(18),
B DATE,
C NUMBER(18) DEFAULT 1
);
TYPE TCURSOR IS REF CURSOR;
type CUR_TAB is table of REC;
procedure CUR_PROC(CUR OUT TCURSOR, SEL_TYPE IN NUMBER); --some procedure with undefined columns
FUNCTION CUR_TAB_PIPLINED(P_SEL_TYPE IN NUMBER) RETURN MYPKG.CUR_TAB PIPELINED;
END MYPKG;
/
CREATE OR REPLACE package body MYPKG
as
function CUR_TAB_PIPLINED(P_SEL_TYPE IN NUMBER) return MYPKG.CUR_TAB PIPELINED
as
TAB_DATA CUR_TAB;
TEMP_CUR TCURSOR;
begin
MYPKG.CUR_PROC(TEMP_CUR, P_SEL_TYPE);
fetch TEMP_CUR bulk collect into TAB_DATA;
close TEMP_CUR;
pipe row(TAB_DATA(1)); --getting 1 row is enough for example
end;
PROCEDURE CUR_PROC (CUR OUT TCURSOR, SEL_TYPE IN NUMBER) as
BEGIN
IF SEL_TYPE = 1 THEN
OPEN CUR FOR SELECT 1 AS A, SYSDATE AS B FROM dual;
ELSE
OPEN CUR FOR SELECT 1 AS A, SYSDATE AS B, 5 AS C FROM dual;
END IF;
end;
END MYPKG;
/
SELECT * FROM TABLE(MYPKG.CUR_TAB_PIPLINED(1)); --ORA-00942
SELECT * FROM TABLE(MYPKG.CUR_TAB_PIPLINED(2)); --works good
Upvotes: 1
Views: 617
Reputation: 168578
You need to fetch the correct number of columns for the cursor:
CREATE OR REPLACE package body MYPKG
as
function CUR_TAB_PIPLINED(P_SEL_TYPE IN NUMBER) return MYPKG.CUR_TAB PIPELINED
as
v_a NUMBER(18);
v_b DATE;
v_c NUMBER(18);
TEMP_CUR TCURSOR;
begin
MYPKG.CUR_PROC(TEMP_CUR, P_SEL_TYPE);
IF p_sel_type = 2 THEN
LOOP
FETCH temp_cur INTO v_a, v_b, v_c;
EXIT WHEN temp_cur%NOTFOUND;
PIPE ROW (REC(v_a, v_b, v_c));
END LOOP;
ELSE
LOOP
FETCH temp_cur INTO v_a, v_b;
EXIT WHEN temp_cur%NOTFOUND;
PIPE ROW (REC(v_a, v_b, 1));
END LOOP;
END IF;
CLOSE TEMP_CUR;
EXCEPTION
WHEN NO_DATA_NEEDED THEN
CLOSE TEMP_CUR;
end;
PROCEDURE CUR_PROC (CUR OUT TCURSOR, SEL_TYPE IN NUMBER) as
BEGIN
IF SEL_TYPE = 1 THEN
OPEN CUR FOR SELECT 1 AS A, SYSDATE AS B FROM dual;
ELSE
OPEN CUR FOR SELECT 1 AS A, SYSDATE AS B, 5 AS C FROM dual;
END IF;
end;
END MYPKG;
/
Note: you also need to ensure the cursor is closed if you stop calling the pipelined function before the cursor is exhausted (i.e. catch the NO_DATA_NEEDED
exception).
db<>fiddle here
Upvotes: 1