Multiple functions calling a same function. I need to create a flag - sql

Multiple functions calling a same function. I need to create a flag.
Suppose I have 4 functions(f1,f2,f3 and f4) in a PLSQL package.
F1 is being called by F2,F3 and F4. All I want a flag in order to track which function call the F1.
For example . If f2 called f1 then flag=2,flag=3 when f3 called f1 and so on and this should be accessible in such way that I can this flag inside function F1.

You don't need to pass any flag, as PL/SQL can tell you the call stack.
create or replace package demo
as
function f1 return number;
function f2 return number;
function f3 return number;
function f4 return number;
end demo;
create or replace package body demo
as
function f1 return number
is
this_unit varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
caller varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(2));
begin
dbms_output.put_line(this_unit || ' called from ' || caller);
return 1;
end f1;
function f2 return number
is
this_unit varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
caller varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(2));
begin
dbms_output.put_line(this_unit || ' called from ' || caller);
return f1 * 2;
end f2;
function f3 return number
is
this_unit varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
caller varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(2));
begin
dbms_output.put_line(this_unit || ' called from ' || caller);
return f1 + f2;
end f3;
function f4 return number
is
this_unit varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
caller varchar2(257) := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(2));
begin
dbms_output.put_line(this_unit || ' called from ' || caller);
return f2 * 2;
end f4;
end demo;
Test:
declare
n number;
begin
n := demo.f3;
end;
/
DEMO.F3 called from __anonymous_block
DEMO.F1 called from DEMO.F3
DEMO.F2 called from DEMO.F3
DEMO.F1 called from DEMO.F2
You can adjust the calls to util_call_stack to exclude the package name if you only want the function names.

Related

how to update a blob column containing XML data in oracle

i have tried below two approaches:
update table set blobcolname='<?xml>...';
got this error: "string literal tool long" - since the xml value that i am inserting is tool long
2.
DECLARE
str varchar2(32767);
BEGIN
str:='<?xml>...';
update table set blobcolname = str;
END;
/
got this error: ORA-01461:can bind a LONG value only for insert into a LONG column
If you have an option to install two functions in your database, I would use this:
First: I create a function to convert a clob to a blob object
create or replace function clob_to_blob (p1_clob CLOB) return BLOB is
Result BLOB;
o1 integer;
o2 integer;
c integer;
w integer;
begin
o1 := 1;
o2 := 1;
c := 0;
w := 0;
DBMS_LOB.CreateTemporary(Result, true);
DBMS_LOB.ConvertToBlob(Result, p1_clob, length(p1_clob), o1, o2, 0, c, w);
return(Result);
end clob2blob;
/
Second: I create a function to the opposite, convert a blob to a clob
create or replace function blob_to_char (p1_blob BLOB)
return clob is
out_clob clob;
n number;
begin
if (p1_blob is null) then
return null;
end if;
if (length(p1_blob)=0) then
return empty_clob();
end if;
dbms_lob.createtemporary(out_clob,true);
n:=1;
while (n+32767<=length(p1_blob)) loop
dbms_lob.writeappend(out_clob,32767,utl_raw.cast_to_varchar2(dbms_lob.substr(p1_blob,32767,n)));
n:=n+32767;
end loop;
dbms_lob.writeappend(out_clob,length(p1_blob)-n+1,utl_raw.cast_to_varchar2(dbms_lob.substr(p1_blob,length(p1_blob)-n+1,n)));
return out_clob;
end;
/
Third: To verify that I can insert
declare
str clob;
BEGIN
str :='<?xml>...';
update table set blobcolname = clob_to_blob ( str );
END;
/
Lastly: To make a regression test and check that I got the xml as it was originally
select blob_to_char( blobcolname ) from your table;

Adding output to function PL/SQL

I am trying to get my function to include the date and current user, but I keep getting an error that my function is in an invalid state. If I comment out or remove the two dbms_output... lines it works just fine. Any ideas of how to return that output? Using Oracle SQL Developer
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
str2 VARCHAR2(10) := y;
BEGIN
RETURN str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;
You have to declare the result Variable first, also enable the dbms_output on your SQL Developer.
This should work.
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
STR2 VARCHAR2(10) := Y;
result VARCHAR2(250);
BEGIN
result := str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result;
END;
/
SELECT F_CONCATENATE_STRINGS('Crypto','Currency') FROM DUAL;
your function has some issues.
result is not declared
return statement should be the last statement in the function. it return a result and terminates the execution of the function
you should convert to char the sysdate
you can try this one:
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
--str1 VARCHAR2(10) := x;
--str2 VARCHAR2(10) := y;
result VARCHAR2(20);
BEGIN
result := x || y;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || to_char(SYSDATE,'dd.mm.yyyy') || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result; -- move to the end of the function
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;

Function with Table_name as input and returns a number in Oracle

I have a function that takes table_name as input and returns a number.
The function gets compiled properly but for some reason when I try test the function it throws error - missing keyword.
CREATE OR replace FUNCTION TEST_FUNCTION (name_table IN VARCHAR2) RETURN NUMBER
IS
rday NUMBER;
BEGIN
execute immediate
'select day_i into rday
FROM ' || name_table || '
WHERE day_i = 1 and rownum = 1';
return rday;
END TEST_FUNCTION;
This is how I am testing it Select TEST_FUNCTION ('FDR_REP') from dual;
The syntax for execute immediate is different for an into clause
try
CREATE OR replace FUNCTION TEST_FUNCTION (name_table IN VARCHAR2) RETURN NUMBER
IS
rday NUMBER;
BEGIN
execute immediate 'select day_i
FROM ' || name_table || '
WHERE day_i = 1 and rownum = 1' into rday;
return rday;
END TEST_FUNCTION;

PL sql to find a string in a list

I have a problem because in this code I don't know how I can to test if a string is in a list which isn't initialized .
The code is this :
TYPE t1 IS TABLE OF VARCHAR2(32767) index by PLS_INTEGER;
v_t1 t1;
WOUT varchar2(80) :='bbbb';
v_t1(1):='bbbb';
if (WOUT member of v_t1 ) then
....
end if;
I don't know how i can write in plsql the condition that wout is inside v_t1 because member is accepted only with lists initialized.
Thanks for the help
With the collection declared as you have it there's no simple way to do what you're trying to do except to iterate through the collection to find the element you want:
declare
TYPE t1 IS TABLE OF VARCHAR2(32767)
index by PLS_INTEGER;
v_t1 t1;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN := FALSE;
BEGIN
v_t1(0) := 'aaaa';
v_t1(1) := 'bbbb';
v_t1(2) := 'cccc';
v_t1(26) := 'zzzz';
i := v_t1.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE('i=' || i);
IF v_t1(i) = WOUT THEN
bFound := TRUE;
EXIT;
END IF;
i := v_t1.NEXT(i);
IF i IS NULL THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
end;
HOWEVER - if you change the collection so that it's indexed by the string it contains you can accomplish this task a bit easier by using the EXISTS collection method:
declare
TYPE T2 IS TABLE OF VARCHAR2(32767)
INDEX BY VARCHAR2(32767);
v_t2 t2;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN;
BEGIN
v_t2('aaaa') := 'aaaa';
v_t2('bbbb') := 'bbbb';
v_t2('cccc') := 'cccc';
v_t2('zzzz') := 'zzzz';
bFound := v_t2.EXISTS(WOUT);
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
END;
The documentation on collection methods can be found here.
Share and enjoy.
You can create special function that checks if such value is inside collection. Something like that:
FUNCTION exist_in_collection( p_table t1, p_text VARCHAR2) RETURN boolean
IS
BEGIN
IF p_table.LAST IS NULL THEN
RETURN FALSE;
ELSE
FOR i IN p_table.FIRST.. p_table.LAST
LOOP
IF p_table(i) = p_text THEN
RETURN TRUE;
END IF;
END LOOP;
RETURN FALSE;
END IF;
END exist_in_collection;
if p_table is not initialized LAST is NULL you will get FALSE. And then you can use this function to check whether the string is inside collection:
IF exist_in_collection(p_table, WOUT) THEN ... END IF;

Function : PLS-00306: wrong number or types of arguments in call to 'WORK_DAYS'

I am trying to create a package that includes both a function and procedure, with the function being called in the procedure. I feel like my code below should work but I keep getting the error PLS-00306: wrong number or types of arguments in call to 'WORK_DAYS'. Any advice on what the issue is?
I also think that when this will run it will return more than one row, anyway around throwing an error? I have used a For loop in the procedure but will I also need one in the function?
create or replace PACKAGE BODY WORK_ALLOCATION_OVERDUE IS
FUNCTION WORK_DAYS
(P_EMP_ID IN NUMBER)
RETURN NUMBER
IS
TOTAL NUMBER;
BEGIN
SELECT ROUND(WORK_END_DATE - SYSDATE)
INTO TOTAL
FROM PL_WORK_ALLOCATION
WHERE P_EMP_ID = D_EMP_ID;
RETURN TOTAL;
END WORK_DAYS;
PROCEDURE WORK_DAYS_INFO
(P_EMP_ID IN NUMBER)
IS
CURSOR CUR_CRIME IS
SELECT S_REPORTED_CRIME_ID, WORK_DESC, WORK_END_DATE
FROM PL_WORK_ALLOCATION
WHERE P_EMP_ID = D_EMP_ID
GROUP BY D_EMP_ID, S_REPORTED_CRIME_ID, WORK_DESC, WORK_END_DATE;
WORK_FLAG VARCHAR2(10);
BEGIN
FOR REC_CRIME IN CUR_CRIME LOOP
IF WORK_DAYS < 1 THEN
WORK_FLAG := 'OVERDUE';
ELSIF WORK_DAYS > 1 THEN
WORK_FLAG := 'DUE';
END IF;
IF WORK_FLAG = 'OVERDUE' THEN
DBMS_OUTPUT.PUT_LINE('Reported Crime: ' || REC_CRIME.S_REPORTED_CRIME_ID || ' Work Desc: ' || REC_CRIME.WORK_DESC || ' is ' || WORK_FLAG);
END IF;
END LOOP;
END WORK_DAYS_INFO;
END;​
You created WORK_DAYS as a function with 1 input parameter, but aren't supplying it that parameter anywhere in your procedure. You need to pass it the input parameter P_EMP_ID like so - WORK_DAYS (P_EMP_ID) before you can use it.
As has been said already, there is unecessary work being carried out in your package. You could refactor to this:
CREATE OR REPLACE PACKAGE BODY WORK_ALLOCATION_OVERDUE IS
FUNCTION WORK_DAYS
(P_WORK_END_DATE IN DATE)
RETURN NUMBER
IS
RETURN ROUND(P_WORK_END_DATE - SYSDATE);
END WORK_DAYS;
PROCEDURE WORK_DAYS_INFO
(P_EMP_ID IN NUMBER)
IS
CURSOR CUR_CRIME IS
SELECT S_REPORTED_CRIME_ID, WORK_DESC, WORK_END_DATE
FROM PL_WORK_ALLOCATION
WHERE P_EMP_ID = D_EMP_ID
GROUP BY D_EMP_ID, S_REPORTED_CRIME_ID, WORK_DESC, WORK_END_DATE;
WORK_FLAG VARCHAR2(10);
BEGIN
FOR REC_CRIME IN CUR_CRIME LOOP
IF WORK_DAYS(REC_CRIME.WORK_END_DATE) < 1 THEN
WORK_FLAG := 'OVERDUE';
ELSIF WORK_DAYS(REC_CRIME.WORK_END_DATE) > 1 THEN
WORK_FLAG := 'DUE';
END IF;
IF WORK_FLAG = 'OVERDUE' THEN
DBMS_OUTPUT.PUT_LINE('Reported Crime: ' || REC_CRIME.S_REPORTED_CRIME_ID || ' Work Desc: ' || REC_CRIME.WORK_DESC || ' is ' || WORK_FLAG);
END IF;
END LOOP;
END WORK_DAYS_INFO;
or, just take out the function completely:
CREATE OR REPLACE PACKAGE BODY WORK_ALLOCATION_OVERDUE IS
PROCEDURE WORK_DAYS_INFO
(P_EMP_ID IN NUMBER)
IS
CURSOR CUR_CRIME IS
SELECT S_REPORTED_CRIME_ID,
WORK_DESC,
WORK_END_DATE,
ROUND(SUM(WORK_END_DATE - SYSDATE)) WORK_DAYS
FROM PL_WORK_ALLOCATION
WHERE P_EMP_ID = D_EMP_ID
GROUP BY D_EMP_ID, S_REPORTED_CRIME_ID, WORK_DESC, WORK_END_DATE;
WORK_FLAG VARCHAR2(10);
BEGIN
FOR REC_CRIME IN CUR_CRIME LOOP
IF REC_CRIME.WORK_DAYS < 1 THEN
WORK_FLAG := 'OVERDUE';
ELSIF REC_CRIME.WORK_DAYS > 1 THEN
WORK_FLAG := 'DUE';
END IF;
IF WORK_FLAG = 'OVERDUE' THEN
DBMS_OUTPUT.PUT_LINE('Reported Crime: ' || REC_CRIME.S_REPORTED_CRIME_ID || ' Work Desc: ' || REC_CRIME.WORK_DESC || ' is ' || WORK_FLAG);
END IF;
END LOOP;
END WORK_DAYS_INFO;
However, you do have a problem in that P_EMP_ID appears to be returning more than one row (hence your ORA-01422 exception).