Nested PIPELINED function - oracle

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.

Related

PL/SQL "all_search" procedure is not a procedure or is undefined

This function is inside a package, but when I call the function the following error appears: PL/SQL "all_search" is not a procedure or is undefined. Someone can help me?
CREATE OR REPLACE PACKAGE employee_tab IS
FUNCTION all_search (ID_EMP in NUMBER) RETURN O_T_EMPL PIPELINED;
END employee_tab;
/
CREATE OR REPLACE TYPE O_T_EMPL AS TABLE OF O_EMPLOYEE;
/
CREATE OR REPLACE PACKAGE BODY employee_tab IS
FUNCTION all_search (ID_EMP in NUMBER) RETURN O_T_EMPL PIPELINED
IS
TAB_OBJC_EMP O_T_EMPL;
MY_QUERY_SEARCH VARCHAR2(400);
REF_C SYS_REFCURSOR;
MAX_ROW NUMBER := 25;
BEGIN
MY_QUERY_SEARCH := 'SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID = ID_EMP';
open REF_C for MY_QUERY_SEARCH using ID_EMP;
loop
--
fetch REF_C bulk collect into TAB_OBJC_EMP limit MAX_ROW;
exit when TAB_OBJC_EMP.count = 0;
for i in 1..TAB_OBJC_SEE.count
--
loop
pipe row(O_EMPLOYEE(TAB_OBJC_EMP(i).V_O_EMP_ID,
TAB_OBJC_EMP(i).V_O_HIRE_ID,
TAB_OBJC_EMP(i).V_O_DEP_ID)
);
end loop;
--
END loop;
--
CLOSE REF_C;
RETURN;
--
END all_search;
END employee_tab;
/
call function: employee_tab.all_search(1);
You need to assign the result of the function to something, try something like:
DECLARE
l_emp_id NUMBER;
BEGIN
SELECT EMP_ID
INTO l_emp_id
FROM TABLE(employee_tab.all_search(1))
WHERE rownum = 1;
DBMS_OUTPUT.put_line(TO_CHAR(EMP_ID));
END;
/

Pipelining Between PL/SQL Table Functions

I have a package with 2 pipelined functions. When I'm trying to call one function with another function as it's argument I'm getting "ORA-06553: PLS-306: wrong number or types of arguments in call" error.
Here is the package:
create or replace NONEDITIONABLE TYPE RESULTING_RECORD_RT as object
(
CALENDAR NVARCHAR2(1024),
PRODUCT NVARCHAR2(1024),
MEASURE NVARCHAR2(1024),
VALUE NUMBER
);
/
create or replace NONEDITIONABLE TYPE RESULTING_COLS_RT IS TABLE OF RESULTING_RECORD_RT;
/
create or replace package pipe_pkg as
function pipe_func_emp return RESULTING_COLS_RT PIPELINED;
function pipe_func_emp2(input_Set IN resulting_cols_rt) return RESULTING_COLS_RT PIPELINED;
end;
/
create or replace package body pipe_pkg as
function pipe_func_emp return RESULTING_COLS_RT
PIPELINED
is
test_tbl resulting_cols_rt:= resulting_cols_rt();
begin
test_tbl.extend;
test_tbl(1):=resulting_record_rt('A','B','C',1);
test_tbl.extend;
test_tbl(2):=resulting_record_rt('A','B','D',2);
PIPE ROW(test_tbl(1));
PIPE ROW(test_tbl(2));
return;
end;
function pipe_func_emp2(input_Set IN resulting_cols_rt) return RESULTING_COLS_RT
PIPELINED
is
v_tmp NVARCHAR2(10240);
l_res SYS_REFCURSOR;
recs resulting_record_rt;
begin
open l_res for select * from table(input_Set);
loop
fetch l_res into recs;
PIPE ROW(recs);
exit when l_res%notfound;
end loop;
close l_res;
return;
end;
end;
/
I'm calling the functions as follows:
select * from TABLE(pipe_pkg.pipe_func_emp2(CURSOR(select * from TABLE(pipe_pkg.pipe_func_emp()))));
And the call throws error:
ORA-06553: PLS-306: wrong number or types of arguments in call to 'PIPE_FUNC_EMP2'
06553. 00000 - "PLS-%s: %s"
What am I doing wrong?
The function pipe_func_emp2 expected RESULTING_COLS_RT as it's argument, but got REF CURSOR. These are incompatible types.
Try following reproducible example of a chaining of the pipelined functions:
create or replace type somerow as object (id int, val varchar2 (8))
/
create or replace type sometab is table of somerow
/
create or replace package pack as
function func1 return sometab pipelined;
function func2 (cur sys_refcursor) return sometab pipelined;
end;
/
create or replace package body pack as
function func1 return sometab pipelined is
tab sometab := sometab (somerow (1,'AAA'), somerow (2,'BBB'));
begin
for i in 1..tab.count loop
pipe row (tab(i));
end loop;
return;
end;
function func2 (cur sys_refcursor) return sometab pipelined is
sr somerow;
begin
loop
fetch cur into sr;
exit when cur%notfound;
pipe row (sr);
end loop;
close cur;
return;
end;
end;
/
The query and it's outcome:
select *
from table (pack.func2 (
cursor (select value (p) from table (pack.func1()) p )))
/
ID VAL
---------- --------
1 AAA
2 BBB

Pipelined function with a parameter declare with %ROWTYPE

I'm trying to declare a pipelined table function (t) inside a package that takes an argument declared as <tablename>%ROWTYPE. Declaring that function works and the package compiles without any error.
But I would like to use this function inside a procedure (p1) like shown below.
CREATE OR REPLACE PACKAGE BODY t1
AS
-- private
PROCEDURE p1
IS
l_person persons%ROWTYPE;
BEGIN
FOR l_row IN (SELECT *
FROM TABLE (t (l_person)))
LOOP
NULL;
END LOOP;
END;
-- public
FUNCTION t (p_persons_record persons%ROWTYPE)
RETURN t_a_list
PIPELINED
IS
l_a t_a;
BEGIN
l_a.dummy := 'A';
PIPE ROW (l_a);
END;
END;
This sample code does not makes sense but it demonstrates my problem.
It just doesn't compile but gives the following errors:
[Error] PLS-00382 (10: 38): PLS-00382: expression is of wrong type
[Error] PLS-00306 (10: 35): PLS-00306: wrong number or types of arguments in call to 'T'
[Error] ORA-00904 (10: 35): PL/SQL: ORA-00904: "T1"."T": invalid identifier
Can anyone explain what's wrong and how to fix those errors?
Edit:
The package spec is:
CREATE OR REPLACE PACKAGE t1
AS
TYPE t_a IS RECORD (dummy VARCHAR2 (1));
TYPE t_a_list IS TABLE OF t_a;
FUNCTION t (p_persons_record persons%ROWTYPE)
RETURN t_a_list
PIPELINED;
END;
You cannot use a record type in SQL Scope. So a PL/SQL function with a record parameter derived by rowtype attribute cannot be used as table function in SQL.
The only thing you have to rewrite is to use a SQL object on schema level instead of a PL/SQL record.
-- adapt your columns to your table as necessary
CREATE OR REPLACE TYPE g_persons AS OBJECT (
ID int,
C1 int
);
CREATE OR REPLACE PACKAGE t1
AS
TYPE t_a IS RECORD (dummy VARCHAR2 (1));
TYPE t_a_list IS TABLE OF t_a;
FUNCTION t (p_persons_record g_persons_t)
RETURN t_a_list
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY t1
AS
-- private
PROCEDURE p1
IS
l_person g_persons_t;
BEGIN
l_person.ID := 1; -- init your record some how
l_person.C1 := 1; -- init your record some how
FOR l_row IN (SELECT *
FROM TABLE (t (l_person)))
LOOP
NULL;
END LOOP;
END;
-- public
FUNCTION t (p_persons_record g_persons_t)
RETURN t_a_list
PIPELINED
IS
l_a t_a;
BEGIN
l_a.dummy := 'A';
PIPE ROW (l_a);
END;
END;
/
I think your PACKAGE specification lacks t's declaration. Have you checked that your PACKAGE specification is something similar to this?:
CREATE OR REPLACE PACKAGE t1 AS
PROCEDURE p1;
FUNCTION t (p_persons_record persons%ROWTYPE) RETURN t_a_list PIPELINED;
END;

Executing functions in sequence defined in a package

I've been given the task of converting a stored procedure, with several CRUD operations, to a package with functions defined.
When I created the package in Oracle SQL Developer, I defined may functions:
FUNCTION func1 RETURN NUMBER;
FUNCTION func2 RETURN NUMBER;
etc...
I have corresponding code in the 'package body':
FUNCTION func1 RETURN NUMBER
IS
BEGIN
-- some CRUD operation
END;
RETURN 0;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('FUNCTION func1: error ' || ERR_NUM || ', Msg:' || ERR_MSG);
ROLLBACK;
END func1;
When I run them, I can choose which function to run from the list supplied. My question is what do I need to write or do to have all functions run in sequence with one command?
thanks
If you need to run some procedures in a given order, you need to build a procedure or an anonymous block that calls the procedures in the right order.
For example:
create or replace package aPackage is
procedure runAll;
end;
create or replace package body aPackage is
procedure proc1 is
begin
dbms_output.put_line('Procedure 1');
end proc1;
--
procedure proc2 is
begin
dbms_output.put_line('Procedure 2');
end proc2;
procedure runAll is
begin
proc1;
--
proc2;
--
end runAll;
end;
The call:
SQL> exec aPackage.runAll;
Procedure 1
Procedure 2
PL/SQL procedure successfully completed.
SQL>
I used procedures just to call them without need to assign the result to a variable, but the idea is the same for functions.
With functions:
create or replace package aPackageFun is
function runAll return number;
end;
create or replace package body aPackageFun is
function fun1 return number is
begin
dbms_output.put_line('Inside function 1');
return 1;
end fun1;
--
function fun2 return number is
begin
dbms_output.put_line('Inside function 2');
return 2;
end fun2;
function runAll return number is
vNum1 number;
vNum2 number;
begin
vNum1 := fun1();
--
vNum2 := fun2();
--
return vNum1 + vNum2;
end runAll;
end;
The call:
SQL> select aPackageFun.runAll() from dual;
APACKAGEFUN.RUNALL()
--------------------
3
Inside function 1
Inside function 2
SQL>
About your code, notice that you have a commit after a return: the commit will never be executed.

Nested PIPELINED function in pl/sql

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".

Resources