Nested PIPELINED function in pl/sql - oracle

I have to write a nested pipelined function in pl/sql which I tried implementing in the following manner.
create package body XYZ AS
function main_xyz return data_type_1 pipelined is
begin
--code
pipe row(sub_func);
end;
function sub_func return data_type_1 pipelined is
begin
--code
pipe row(sub_func_1);
end;
function sub_func_1 return data_type_1 pipelined is
begin
--code
pipe row(main_abc);
end;
end;
create package body abc AS
function main_abc return data_type_2 pipelined is
var data_type_2;
begin
--code
return var;
end;
end;
However, I get the following error
[Error] PLS-00653 : PLS-00653: aggregate/table functions are not
allowed in PL/SQL scope
Where am I going wrong? Is it syntax or logic?

Pipelined functions provide rows one by one (on demand), so you cannot put all the rows at one time from pipelined function.
Seems to me you need to change main_xyz this way:
function main_xyz return data_type_1 pipelined is
begin
--code
FOR rec IN (select * from table(XYZ.sub_func)) LOOP
pipe row(rec);
END LOOP;
end;
Consider that sub_func must be in specification of XYZ package since everything you use in SQL queries including PIPELINED functions are to be public (i.e. visible to a user who runs query).
UPDATE: I forget to alert: do not abuse pipelined functions (if you have another choice) - queries using them might have lame performance because DB engine cannot build a good execution plan for "unpredictable piped rows".

Related

Trying to call a Function inside a stored procedure in oracle

i am trying to call a function from stored procedure in Oracle, but not getting any idea how to do.
my function has two IN parameter and one OUT parameter.
in my procedure i am using out sys refcursor . Any refrence or example will help me a lot.
Here is a simple example for calling function inside procedure. Also as mentioned by APC using OUT in function is a bad practice. Instead you can return your required output. And I'm not sure how you are using sys_refcursor, so modify your procedure accordingly
CREATE OR REPLACE FUNCTION SUM_OF_2(NUM1 IN NUMBER,NUM2 IN NUMBER) RETURN NUMBER
IS
RESULT_SUM NUMBER;
BEGIN
RESULT_SUM:=NUM1+NUM2;
RETURN RESULT_SUM;
END;
CREATE OR REPLACE PROCEDURE CALL_FUNCTON(NUM1 NUMBER,NUM2 NUMBER)
AS
V_FINAL_RESULT NUMBER;
BEGIN
V_FINAL_RESULT:=SUM_OF_2(NUM1,NUM2);
DBMS_OUTPUT.PUT_LINE(V_FINAL_RESULT);
END;
BEGIN
CALL_FUNCTON(5,10);
END;
/
CHECK DEMO HERE
Not sure on what your requirement is , maybe you are just trying the code for education purposes. Generally I have not seen much code which uses OUT parameter with functions, in case you want to return multiple values to the caller object then you could use a procedure with more then one OUT variables. There are some limitation on how an oracle function with OUT parameter would differ from a normal function.
CREATE OR REPLACE FUNCTION temp_demo_func(out_var1 OUT NUMBER)
RETURN VARCHAR2 IS
BEGIN
out_var1 := 1;
RETURN 'T';
EXCEPTION
WHEN OTHERS THEN
RETURN 'F';
END temp_demo_func;
/
CREATE OR REPLACE PROCEDURE temp_demo_proc
(
in_var1 NUMBER
,cur_refcur_out OUT SYS_REFCURSOR
) IS
res VARCHAR2(1);
out_var1 NUMBER;
BEGIN
res := temp_demo_func(out_var1 => out_var1);
dbms_output.put_line(out_var1);
OPEN cur_refcur_out FOR
SELECT in_var1
,out_var1
,res
FROM dual;
END;
/
set serveroutput on
declare
cur_refcur_out Sys_Refcursor;
in_var1 number := 22;
begin
temp_demo_proc(in_var1 => in_var1
,cur_refcur_out => cur_refcur_out);
end;
/

Oracle PL/SQL: Call DML procedure from a function

I have a procedure that has DML commands. the procedure accepts a variable of type out, and it returns a value.
i need call this procedure from a function.
the goal isĀ  that the function will return the value of the variable out returns the procedure.
(i need it for SSIS, but I believe that it is useful in other cases.)
During attempts, I got these errors:
ORA-14551: cannot perform a DML operation inside a query tips.
ORA-06519: active autonomous transaction detected and rolled back.
I'm looking for the right syntax to do it.
Example of a solution that works:
The procedure:
create or replace procedure MyProc(outRes OUT NUMBER)
is
begin
update some_table set some_description = 'abc';
commit;
if (some_condition) then
outRes := 666;
else
outRes := 999;
end if;
end MyProc;
Note: You must do commit; at the end of the DML command.
The function:
CREATE or replace FUNCTION MyFunc
RETURN int IS
PRAGMA AUTONOMOUS_TRANSACTION;
myVar number;
begin
MyProc(myVar);
return myVar;
END MyFunc;
Note that at the beginning of the function has: PRAGMA AUTONOMOUS_TRANSACTION;
And this function call:
select MyFunc() as res from dual;
Here is an example of what you need to do. Do know this is UNTESTED, but should give you a general idea of which way to go. This is known as Dynamic SQL and uses bind variables. There's a lot more I don't know such as the data type your procedure spits out and what not... so if it's not varchar2 then change it accordingly...
FUNCTION myFunc(procedure_call varchar2) RETURN VARCHAR2
IS
v_out1 varchar2(500);
BEGIN
EXECUTE IMMEDIATE 'begin '||procedure_call||'( :out1 ); end;' using v_out1;
RETURN v_out;
END;

Nested PIPELINED function

create type data_type_1 as object (x number, y number)
/
create type table_type_1 as table of data_type_1
/
create or replace package xyz AS
function main_xyz return table_type_1 pipelined;
function sub_func return table_type_1 pipelined;
function sub_func1 return table_type_1 pipelined;
end xyz;
/
create package body XYZ AS
function main_xyz return data_type_1 pipelined is
begin
--code
--pipe row(sub_func); --edit_1
FOR rec in (select * from table(sub_func1(x,y))) LOOP
pipe row(rec);
END LOOP;
end;
--function sub_func return data_type_1 pipelined is --edit_1
--begin --edit_1
--code --edit_1
--pipe row(def); --def is data_type_1 --edit_1
--end; --edit_1
function sub_func_1(x in number, y in number) return data_type_1 pipelined is
begin
--code
loop
pipe row(abc); --abc is data_type_1
end loop;
end;
end;
create package body ABC AS
function main_ABC is
begin
--code
FOR rec in (select * from table(main_xyz)) LOOP
pipe row(rec);
END LOOP;
end;
end;
Error that I obtain is...
Error is showed in the block of main_xyz where sub_func1 is called.
[Error] PLS-00382 (): PLS-00382: expression is of wrong type
[Error] PLS-00306 (): PLS-00306: wrong number or types of arguments in call to
[Error] ORA-00904 (): PL/SQL: ORA-00904: : invalid identifier
[Error] PLS-00364 (): PLS-00364: loop index variable 'REC' use is invalid
What is wrong in the above code? and why?
Your functions are returning data_type_1, and the table collection is trying to consume that too. But both need a collection type, even if you expect them to only return a single value (in which case there isn't much point pipelining). You can't pipe a collection type directly, you pipe a member of the collection. So data_type_1 should be a scalar or object/record type, and you need another type which is a collection of those.
create type data_type_1 as object (x number, y number)
/
create type table_type_1 as table of data_type_1
/
create or replace package xyz AS
function main_xyz return table_type_1 pipelined;
function sub_func return table_type_1 pipelined;
function sub_func1 return table_type_1 pipelined;
end xyz;
/
create or replace package body xyz as
function main_xyz return table_type_1 pipelined is
begin
--code
for rec in (select * from table(sub_func)) loop
pipe row(data_type_1(rec.x, rec.y));
end loop;
for rec in (select * from table(sub_func1)) loop
pipe row(data_type_1(rec.x, rec.y));
end loop;
end;
function sub_func return table_type_1 pipelined is
def data_type_1;
begin
--code
pipe row(def); --def is data_type_1
end sub_func;
function sub_func1 return table_type_1 pipelined is
abc data_type_1;
begin
--code
loop
pipe row (abc); --abc is data_type_1
end loop;
end sub_func1;
end xyz;
/
So I've added a table type of your existing data_type_1, and changed the function definitions to return that table type instead. The pipe row still uses data_type_1 - each is a row in the table type. Your loop needs a query for its cursor, not a direct call to table(), so I've changed that too. And the pipe row(sub_func); also needs to be a similar loop over a query.
You only tagged this as PL/SQL but because you may intend to call main_xyz from plain SQL, and because you're calling the sub-functions from a SQL context in those loops, data_type_1 and table_type_1 need to be created at schema level rather than in PL/SQL. (This has changed a bit in 12c but not enough to help here).
If you wanted to have them as PL/SQL types, declared in the package specification, then you couldn't call the function from a non-PL/SQL context, and you'd have to replace the loops with a call to the function followed by an iteration over the returned collection.

Tail call for pipelined functions

I have some pipelined function:
create type my_tab_type as table of ...
create function my_func (X in number) return my_tab_type pipelined as
begin
loop
...
pipe row (...);
end loop;
return;
end;
Now I want to create another pipelined function my_func_zero which does the same as my_func but for fixed value of parameter: my_func_zero must be equivalent to my_func(0).
Can I implement my_func_zero without senseless and boring loop for processing every row returned by select * from table(my_func(0))?
P.S. That thread is a bit similar, but it does not contain answer to my question.
It's possible, but only if you don't declare your second function as pipelined because all functions of this type return results on row-by-row basis.
If you omit this requirement you can reach your target with bulk collect if you need typed cursor:
create function my_zero_func return my_tab_type
as
res_table my_tab_type;
begin
select my_type(field1, field2)
bulk collect into res_table
from table(my_func(0));
return res_table;
end;
Alternatively you can use untyped cursor:
create function my_ref_zero_func return sys_refcursor
as
vRes sys_refcursor;
begin
open vRes for select * from table(my_func(0));
return vRes;
end;
SQLFiddle
In a client application my_ref_zero_func results may be used without changes, but in the SQLFiddle it converted to XML representation because there are no way to demonstrate ref cursor with this tool.

PLSQL functions in packages query

I am having a table named employees..i want to write two functions.1st function by using refcursor it want to fetch all the rows from employess table...and the result wil be shown through 2nd function.These two functions should be in one single package
Your question shows very little industry or effort to work out a solution.
Start by reading the Oracle documentation and it'll help you immensely.
As an example of what you want to achieve:
CREATE OR REPLACE
PACKAGE ref_cur_package
AS
FUNCTION get_emp
RETURN SYS_REFCURSOR;
PROCEDURE show_emp;
END ref_cur_package;
CREATE OR REPLACE
PACKAGE BODY ref_cur_package
AS
FUNCTION get_emp
RETURN SYS_REFCURSOR
IS
emp_rc SYS_REFCURSOR;
BEGIN
OPEN emp_rc
FOR SELECT *
FROM emp;
RETURN emp_rc;
END get_emp;
PROCEDURE show_emp
IS
emp_rc SYS_REFCURSOR;
emp_row emp%ROWTYPE;
BEGIN
emp_rc := get_emp;
LOOP
FETCH emp_rc INTO emp_row;
EXIT WHEN emp_rc%NOTFOUND;
DBMS_OUTPUT.put_line('Employee: '||emp_row.firstname||' '||emp_row.lastname);
END LOOP;
CLOSE emp_rc;
END show_emp;
END ref_cur_package;
/
To see the output you'll need to set the serveroutput on.
You should also add an exception handler to force the closure of the ref cursor if there is a problem but I will leave that to you to research.

Resources