How to give the result of a pipelined function as a default parameter? - oracle

I have created these 4 types :
record type myrecord1 is (...)
record type myrecord2 is (...)
table tableofmyrecord1 is table of myrecord1;
table tableofmyrecord2 is table of myrecord2;
and 2 functions :
function a(k in tableofmyrecord2) return packageName.tableofmyrecord1 PIPELINED;
function b return packageName.tableofmyrecord2 PIPELINED;
I can effectively give a default parameter ; null, the table empty, a table that I have create, but I can give directly the result of a of pipelined function.
function a return packageName.tOfmyrecord(k in tableofmyrecord2 :=package.b) PIPELINED
This does'nt work.
This solution does'nt work too.
declare
defaultArgOFA :=package.b;
function a return packageName.tOfmyrecord(k in tableofmyrecord2 :=defaultArgOFA) PIPELINED
same error
PLS-00653: aggregate/table functions are not allowed

This error says that you cannot call pipelined functions using PLSQL, so you can't directly write v:=a(). But you can use SQL. Below is an example where pipelined b gets input from pipelined a and multiplies salaries.
Package:
create or replace package pkg is
type tr is record (id int, name varchar2(10), sal int);
type tt is table of tr;
function a return tt pipelined;
function b(par in tt) return tt pipelined;
end pkg;
Body:
create or replace package body pkg is
function a return tt pipelined is
v_tr tr;
begin
v_tr.id := 1; v_tr.name := 'Mark'; v_tr.sal := 100;
pipe row (v_tr);
v_tr.id := 2; v_tr.name := 'Pete'; v_tr.sal := 120;
pipe row (v_tr);
return;
end;
function b(par in tt) return tt pipelined is
v_tr tr;
begin
for i in 1..par.last loop
v_tr := par(i);
v_tr.sal := v_tr.sal * 10;
pipe row (v_tr);
end loop;
return;
end;
end pkg;
And this test worked for me:
select * from table(pkg.b(pkg.a));
Result:
ID NAME SAL
------ ---------- ----------
1 Mark 1000
2 Pete 1200

Related

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

PL/SQL Table Function - How to return an empty result set?

I'm trying to create a PL/SQL table function. I don't mind if it is PIPELINED or not. I just want it to return a query-able result set.
And I want to start with an empty result set. (Because it is possible that the result set I intend to construct will be empty.)
Is it possible to create a PL/SQL table function that returns zero rows?
In the books I have, and tutorials I can find, I only see examples that must return at least one record.
Example of the problem:
CREATE OR REPLACE PACKAGE z_util AS
TYPE t_row
IS RECORD (
CATEGORY VARCHAR2( 128 CHAR )
, MEASURE NUMBER
);
TYPE t_tab
IS TABLE OF t_row;
FUNCTION f_test
RETURN t_tab;
END z_util;
/
CREATE OR REPLACE PACKAGE BODY z_util AS
FUNCTION f_test
RETURN t_tab IS
retval t_tab;
BEGIN
RETURN retval;
END;
END z_util;
/
SELECT test.*
FROM TABLE ( z_util.f_test ) test;
Output:
Error starting at line : 24 in command -
SELECT test.*
FROM TABLE ( z_util.f_test ) test
Error at Command Line : 25 Column : 14
Error report -
SQL Error: ORA-00902: invalid datatype
00902. 00000 - "invalid datatype"
*Cause:
*Action:
Something like this?
SQL> CREATE TYPE t_row AS OBJECT (id NUMBER, name VARCHAR2 (50));
2 /
Type created.
SQL> CREATE TYPE t_tab IS TABLE OF t_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION f_test
2 RETURN t_tab
3 AS
4 retval t_tab;
5 BEGIN
6 RETURN retval;
7 END;
8 /
Function created.
SQL> SELECT f_test FROM DUAL;
F_TEST(ID, NAME)
--------------------------------------------------------------------
SQL>
Saying that this is too simple and that it doesn't work while in package:
SQL> CREATE OR REPLACE PACKAGE pkg_test
2 AS
3 FUNCTION f_test
4 RETURN t_tab;
5 END;
6 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY pkg_test
2 AS
3 FUNCTION f_test
4 RETURN t_tab
5 AS
6 retval t_tab;
7 BEGIN
8 RETURN retval;
9 END f_test;
10 END pkg_test;
11 /
Package body created.
SQL> select pkg_test.f_test from dual;
F_TEST(ID, NAME)
-------------------------------------------------------
SQL>
Works OK as well. Did you, by any chance, declare type within the package? If so, try to create it at SQL level.
See 6.4.6 Querying a Collection:
Note: In SQL contexts, you cannot use a function whose return type was declared in a package specification.
You cannot unnest the result of function, but the variable with the same data type:
create or replace package utils as
type t_row is record (category varchar2 (8), measure number);
type t_tab is table of t_row;
function passon (t t_tab:=null) return t_tab;
end utils;
/
create or replace package body utils as
function passon (t t_tab:=null) return t_tab is
begin
return t;
end;
end utils;
/
Usage and outcomes:
var rc refcursor
declare
tab1 utils.t_tab := utils.passon (); -- empty
tab2 utils.t_tab := utils.passon (utils.t_tab (utils.t_row ('category', 50)));
begin
open :rc for
select * from table (tab1) union all
select * from table (tab2);
end;
/
CATEGORY MEASURE
-------- ----------
category 50
jsut use a return statment without any parameters.
I have already one table function that splits a string based in token.
I modified the code for you as an example answet for your quetion.
If you pass the first string null, the function will return no rows, otherwise will return each token in separate row based on the second parameter offcurse.
// create needed types
// row object
CREATE OR REPLACE TYPE T_TOKEN_ROW AS OBJECT (
id NUMBER,
token_text VARCHAR2(50)
);
// table object
CREATE OR REPLACE TYPE T_TOKEN_TAB IS TABLE OF t_token_row;
// create table function to toknize a string
// input : P_string : the string to be toknized
// P_separator : a character to separate tokens
// Outputs : each token in separate record with id field
CREATE OR REPLACE FUNCTION PIPE_tokens (P_string varchar2,P_separator char) RETURN t_token_tab PIPELINED
AS
sLine VARCHAR2(2000);
nPos INTEGER;
nPosOld INTEGER;
nIndex INTEGER;
nLength INTEGER;
nCnt INTEGER;
sToken VARCHAR2(200);
BEGIN
if (P_string is null ) then
return ;
else
sLine := P_string;
IF (SUBSTR(sLine, LENGTH(sLine), 1) <> '|') THEN
sLine := sLine || '|';
END IF;
nPos := 0;
sToken := '';
nLength := LENGTH(sLine);
nCnt := 0;
FOR nIndex IN 1..nLength LOOP
IF ((SUBSTR(sLine, nIndex, 1) = P_separator) OR (nIndex = nLength)) THEN
nPosOld := nPos;
nPos := nIndex;
nCnt := nCnt + 1;
sToken := SUBSTR(sLine, nPosOld + 1, nPos - nPosOld - 1);
PIPE ROW(t_token_row(nCnt,sToken));
--tTokenTab(nCnt) := sToken;
END IF;
END LOOP;
RETURN;
end if;
END;
// 2 Test query
select * from table(PIPE_tokens(null,';')) ;
select * from table(PIPE_tokens('5;2;3',';')) ;

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;

In plsql how can a function use an object declared in a package

i have a package(that is in beta, just want to make it work):
CREATE OR REPLACE PACKAGE pkg1 AS
type pipeDefinition is record
(
str VARCHAR2(4000),
num NUMBER,
);
type pipeContainer is table of pipeDefinition;
FUNCTION f1(x NUMBER) RETURN
pipeContainer pipelined;
END pkg1;
and the package body
CREATE PACKAGE BODY pkg1 AS
FUNCTION f1(x NUMBER) RETURN
pipeContainer pipelined
IS
BEGIN
FOR i IN 1..x LOOP
PIPE ROW(pipeDefinition('i',i));
END LOOP;
RETURN;
END f1;
END pkg1;
my problems:
1if i try to run the above i get
"error at line:
PIPE ROW(pipeDefinition('i',i));
function pipeDefinition can not be found" (not exactly exactly what it said, but close)
2if i try to use pkg1.pipeDefinition instead i get the same error
"error at line:
PIPE ROW(pipeDefinition('i',i));
function pipeDefinition can not be found"
3if i try to add
type pipeDefinition is record
(
str VARCHAR2(4000),
num NUMBER,
);
type pipeContainer is table of pipeDefinition;
in the package body, it sais
"can not declare more than 1 pipeContainer
and more than 1 pipeDefinition"
4if i try to add
type pipeDefinition2 is record
(
str VARCHAR2(4000),
num NUMBER,
);
type pipeContainer2 is table of pipeDefinition2;
in the package body(and alter the pipeRow line with pipeDefinition2), it sais
"the object referenced is of a diferent kind as to the one the function is suposed to output"
5if i try to add 4 and make the function return pipeContainer2 pipelined it sais
"function mismatch package and body"
6if i try to add 6 and make function return pipeContainer2 pipelined in the package also i get
"pipeContainer2 not defined before using it " in the package creator
can anyone give me some hints?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Klas Lindbäck, you are my hero!
Solution:
CREATE OR REPLACE PACKAGE pkg1 AS
type pipeDefinition is record
(
str VARCHAR2(4000),
num NUMBER,
);
type pipeContainer is table of pipeDefinition;
FUNCTION f1(x NUMBER) RETURN
pipeContainer pipelined;
END pkg1;
and the package body
CREATE PACKAGE BODY pkg1 AS
FUNCTION f1(x NUMBER) RETURN
outrec1 pipeDefinition;
--removed: pipeContainer pipelined
IS
BEGIN
FOR i IN 1..x LOOP
--removed: PIPE ROW(pipeDefinition('i',i));
--newLines:
outrec1.str := 'a';
outrec1.num := i;
PIPE ROW(outrec1);
--endNewLines.
END LOOP;
RETURN;
END f1;
END pkg1;
and my new pointer function is finally complete! :"D
SELECT * FROM TABLE(pkg1.f1(5));
|str | num|
a | 1
a | 2
a | 3
a | 4
a | 5
Create or replace package pk2 as
type c1 is ref cursor return employees%rowtype;
type outrec is record (
var_num number,
var1 varchar2(50),
var2 varchar2(50));
type outrecset is table of outrec;
function f_trans (c2 c1) return outrecset pipelined;
end pk2;
create or replace package body pk2 as
function f_trans(c2 c1) return outrecset pipelined is
outrec1 outrec;
outrec2 c2%rowtype;
begin
loop
fetch c2 into outrec2;
exit when c2%notfound;
outrec1.var_num := outrec2.employee_id;
outrec1.var1 := outrec2.first_name;
outrec1.var2 := outrec2.last_name;
PIPE ROW(outrec1);
outrec1.var1 := outrec2.email;
outrec1.var2 := outrec2.phone_number;
PIPE ROW(outrec1);
END LOOP;
RETURN;
END f_trans;
END pk2;
please take hint from this code
data type for the record is not declared

Return 2 values from a PL-SQL function

How can i return 2 values from a PL-SQL function?
I would not advocate creating a function with an OUT parameter for the second value, because I like to think of functions as a pure concept: a function performs an operation on one or more inputs to produce one output. It shouldn't change any of its arguments or have any other "side effects".
So if you need two outputs, write a procedure instead:
procedure get_sqrt_and_half
( p_input number
, p_sqrt OUT number
, p_half OUT number
)
is
begin
p_sqrt := sqrt(p_input);
p_half := p_input/2;
end;
A function can only return a single SQL type, but that can be a user-defined type with multiple values. I'd need to know more about the actual end requirements before I'd recommend this as a solution, but it is a possibility.
create or replace type a_b is object (a number, b number);
/
create or replace function ret_a_b return a_b is
begin
return a_b(1,2);
end;
/
select ret_a_b from dual;
select d.rab.a, d.rab.b from (select ret_a_b rab from dual) d;
You can return one value directly and another one as an OUT parameter. Or you return a record that contains both values. The first option is, in most cases, simpler to do.
**If you are wanting to use it in SQL, then you would need a pipelined function e.g.**
CREATE OR REPLACE TYPE myemp AS OBJECT
( empno number,
ename varchar2(10),
job varchar2(10),
mgr number,
hiredate date,
sal number,
comm number,
deptno number
);
CREATE OR REPLACE TYPE myrectable AS TABLE OF myemp ;
enter code here
CREATE OR REPLACE FUNCTION pipedata(p_min_row number, p_max_row number) RETURN myrectable PIPELINED IS
v_obj myemp := myemp(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
BEGIN
FOR e IN (select *
from (
select e.*
,rownum rn
from (select * from emp order by empno) e
)
where rn between p_min_row and p_max_row)
LOOP
v_obj.empno := e.empno;
v_obj.ename := e.ename;
v_obj.job := e.job;
v_obj.mgr := e.mgr;
v_obj.hiredate := e.hiredate;
v_obj.sal := e.sal;
v_obj.comm := e.comm;
v_obj.deptno := e.deptno;
PIPE ROW (v_obj);
END LOOP;
RETURN;
END;
SQL> select * from table(pipedata(1,5));
Try using OUT parameters:
create or replace function f(a IN NUMBER, b OUT NUMBER) RETURN NUMBER IS
BEGIN
b := a;
RETURN a;
END f;

Resources