Adding function to Oracle is not permament - oracle

I am experiencing weird behavior of Oracle Database 19c. I created user-defined function named raw_to_guid in SqlDeveloper for user 'TEST'. Content of this function is not important really because it's compiling successfully but just in case I share with code below.
create or replace function raw_to_guid( raw_guid in raw ) return varchar2
is
hex varchar2(32);
begin
hex := rawtohex(raw_guid);
return substr(hex, 7, 2)
|| substr(hex, 5, 2)
|| substr(hex, 3, 2)
|| substr(hex, 1, 2)
|| '-'
|| substr(hex, 11, 2)
|| substr(hex, 9, 2)
|| '-'
|| substr(hex, 15, 2)
|| substr(hex, 13, 2)
|| '-'
|| substr(hex, 17, 4)
|| '-'
|| substr(hex, 21, 12);
end
Next step is establish connection to Oracle database from my app and provide login data for user 'TEST'. I have several SELECT statements which use function raw_to_guid and all works perfectly fine.
Now is the clue of my problem:
For first 3-4 calls my SELECT statements works but then (during the same operations) Oracle returns an error Package or function %s is in an invalid state. Again I execute CREATE OR REPLACE FUNCTION raw_to_guid... in SqlDeveloper and everything is working fine for short time but after few minutes the same exception occurs.
It seems like Oracle automatically drops my user-defined function and I do not understand why because I have not any DROP FUNCTION statement in my code.
The same problem occurs when I close database session in my app and create it again.
Is there a way to add this function permamently?

It isn't dropped - you'd get a different error in that case. It is invalidated. Why? No idea.
Here's an example which shows what might have happened (only if your code looked like this):
Procedure (you don't have):
SQL> create or replace procedure p_test is
2 begin
3 null;
4 end;
5 /
Procedure created.
Procedure is called from the function (line #5):
SQL> create or replace function raw_to_guid( raw_guid in raw ) return varchar2
2 is
3 hex varchar2(32);
4 begin
5 p_test;
6 hex := rawtohex(raw_guid);
7
8 return substr(hex, 7, 2)
9 || substr(hex, 5, 2)
10 || substr(hex, 3, 2)
11 || substr(hex, 1, 2)
12 || '-'
13 || substr(hex, 11, 2)
14 || substr(hex, 9, 2)
15 || '-'
16 || substr(hex, 15, 2)
17 || substr(hex, 13, 2)
18 || '-'
19 || substr(hex, 17, 4)
20 || '-'
21 || substr(hex, 21, 12);
22
23 end;
24 /
Function created.
Does it work? Yes:
SQL> select raw_to_guid('abc') res from dual;
RES
----------
BC0A----
SQL>
But, if I drop the procedure, function - which references it - gets invalidated (error you got):
SQL> drop procedure p_test;
Procedure dropped.
SQL> select raw_to_guid('abc') res from dual;
select raw_to_guid('abc') res from dual
*
ERROR at line 1:
ORA-06575: Package or function RAW_TO_GUID is in an invalid state
SQL>
If I recreate the procedure, function recompiles automatically and everything is OK again:
SQL> create or replace procedure p_test is
2 begin
3 null;
4 end;
5 /
Procedure created.
SQL> select raw_to_guid('abc') res from dual;
RES
----------
BC0A----
SQL>
Code you posted (if that's the whole code) probably doesn't suffer from that; substr and rawtohex certainly aren't dropped or were somehow invalidated so that it would cause your function to also be invalidated (as a collateral victim).
What to do? When it fails again, don't just recreate it - check what's wrong by querying user_errors. For example:
SQL> drop procedure p_test;
Procedure dropped.
SQL> select raw_to_guid('abc') res from dual;
select raw_to_guid('abc') res from dual
*
ERROR at line 1:
ORA-06575: Package or function RAW_TO_GUID is in an invalid state
SQL> select line, position, text from user_errors where name = 'RAW_TO_GUID';
LINE POSITION TEXT
---------- ---------- --------------------------------------------------
5 3 PLS-00201: identifier 'P_TEST' must be declared
5 3 PL/SQL: Statement ignored
SQL>
Hopefully, you'll find the culprit.

Related

Read data from a text file and pass that data in a stored procedure input parameter using pl/sql scripts only

I have a text file 'C:\file\Datafile.txt' which contains the below data.
1,Dixon,200
2,Sam,400
3,Peter,200
now I have a SP in oracle which accepts three input parameters (employeeid, employeename, salary).
I want to pass the values in that text file as an input parameter to this Stored Procedure one by one.
Kindly share any pl/sql script if possible to do this activity.
How to enable UTL_FILE package in oracle developer please let me now?
I am thinking of implementing the below solution.
CREATE OR REPLACE PROCEDURE LoadFromFileData(p_FileDir IN VARCHAR2,p_FileName IN VARCHAR2)
AS
v_FileHandle UTL_FILE.FILE_TYPE;
v NewLine VARCHAR2(100); -- Input line
v_Column1 VARCHAR2(100);
v_Column2 VARCHAR2(100);
v_Column3 VARCHAR2(100);
v_FirstConna NUMBER;
v_SecondComma NUMBER;
p_TotalInserted NUMBER;
BEGIN
v_FileHandle := UTL_FILE.FOPEN(p_FileDir ,p_FileName, 'r');
p_TotalInserted := 1;
LOOP
BEGIN
UTL_FILE.GET_LINE(v_FileHandle, v NewLine);
EXCEPTION WHEN NO_DATA_FOUND THEN
EXIT;
END;
v_FirstComma := INSTR(v NewLine, ',', 1, 1);
v_SecondComma := INSTR(v NewLine, ',', 1, 2);
v_Column1 := SUBSTR(v NewLine, 1, v_FirstComma - 1);
v_Column2 := SUBSTR(v RewLine, v_FirstConma + 1, v_SecondComma - v_FirstComma - 1);
v_Column3 := SUBSTR(v Ne;Line, v_SecondConna + 1);
INSERT INTO LECTURER (ID, First_Name, Last_Nane, Major) VALUES (p_TotalInserted, TO_CHAR(v_Column1), TO_CHAR(v_Column2), TO_CHAR(v_Column3));
p_Totallnserted := p_TotalInserted + 1;
END LOOP;
UTL FILE.FCLOSE(v_FileHandle); COMMIT;
EXCEPTION
WHEN UTL FILE. INVALID OPERATION THEN
UTL FIEE.FCLOSE(v FileHandle);
COMMIT;
EXCEPTION
DBMS_OUTPUT.put_line('Uncessusful');
END LoadLecturerData;
As you said that file is on the database server and you'd like to use UTL_FILE, then connect as a privileged user (such as SYS, if you don't have any other) and
create a directory (Oracle object which points to a filesystem directory)
grant read (and/or write) privileges on it to user(s) who will be using it
Why? Because UTL_FILE (as well as e.g. external tables feature, if you choose to use it) require the directory. SQL*Loader, on the other hand, doesn't - with it, file can reside on your local PC, not on the database server.
SQL> show user
USER is "SYS"
SQL> create directory ext_dir as 'c:\temp';
Directory created.
SQL> grant read, write on directory ext_dir to scott;
Grant succeeded.
SQL> grant execute on utl_file to scott;
Grant succeeded.
Now, connect as user which will be doing the job (scott in my case) and - using input file as you specified (c:\temp\datafile.txt) whose content is
1,Dixon,200
2,Sam,400
3,Peter,200
do the following:
SQL> create table test (id number, name varchar2(10), salary number);
Table created.
SQL> declare
2 l_file utl_file.file_type;
3 l_line varchar2(50);
4 begin
5 l_file := utl_file.fopen('EXT_DIR', 'datafile.txt', 'R');
6
7 loop
8 begin
9 utl_file.get_line(l_file, l_line);
10
11 insert into test (id, name, salary) values
12 (substr(l_line, 1, instr(l_line, ',', 1, 1) - 1),
13 substr(l_line, instr(l_line, ',', 1, 1) + 1,
14 instr(l_line, ',', 1, 2) - instr(l_line, ',', 1, 1) - 1),
15 substr(l_line, instr(l_line, ',', 1, 2) + 1)
16 );
17 exception
18 when no_data_found then exit;
19 end;
20 end loop;
21
22 utl_file.fclose(l_file);
23 end;
24 /
PL/SQL procedure successfully completed.
Result:
SQL> select * from test;
ID NAME SALARY
---------- ---------- ----------
1 Dixon 200
2 Sam 400
3 Peter 200
SQL>
Looks OK to me.

How to pick a particular package based on a particular table column value in Oracle?

I have a situation here:
We need to run procedure_1 in a package when a particular column say column X has data in it and simultaneously run another procedure_2 in package if there is no data in that column X
Can anyone please advice on what can be done using Oracle ?
Something like this?
table DECIDE contains PARTICULAR_COLUMN
package has two simple procedures which do nothing; they just identify themselves
.
SQL> create table decide
2 (id number,
3 particular_column varchar2(1));
Table created.
SQL> insert into decide values (1, 'X');
1 row created.
SQL> insert into decide values (2, null);
1 row created.
SQL> create or replace package pkg_decide as
2 procedure p1;
3 procedure p2;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_decide as
2 procedure p1 is
3 begin
4 dbms_output.put_line('-> running proc 1');
5 end;
6
7 procedure p2 is
8 begin
9 dbms_output.put_line('-> running proc 2');
10 end;
11 end;
12 /
Package body created.
SQL>
Testing: if the PARTICULAR_COLUMN isn't empty, P1 will run; otherwise, P2. That can be done with dynamic SQL, i.e. EXECUTE IMMEDIATE. As we're going to run a procedure, we have to form a full anonymous PL/SQL block (begin - procedure name - end).
P.S. As suggested by Matthew, a simple IF will do.
SQL> begin
2 for cur_r in (select particular_column,
3 case when particular_column is not null then 'pkg_decide.p1'
4 else 'pkg_decide.p2'
5 end prc_name
6 from decide
7 ) loop
8 dbms_output.put_line('particular column = ' || cur_r.particular_column ||
9 ', should run procedure ' || cur_r.prc_name);
10 execute immediate 'begin ' ||cur_r.prc_name ||'; end;';
11
12 -- Or, as suggested by Matthew, a simple IF will do
13 if cur_r.particular_column is not null then
14 pkg_decide.p1;
15 else
16 pkg_decide.p2;
17 end if;
18 end loop;
19 end;
20 /
particular column = X, should run procedure pkg_decide.p1
-> running proc 1
-> running proc 1
particular column = , should run procedure pkg_decide.p2
-> running proc 2
-> running proc 2
PL/SQL procedure successfully completed.
SQL>

ORACLE: Creating a oracle procedure with optional parameters

It is common to have pages with filters in most asp.net application (usually have 5 or more criterias, EX: filtr by:lastname, firstname, date, etc..).
Mostly dealing with TSQL (SQL Server 2000/2005) . I would deal with criteria(s) in my procedures, for example:
PARAMETERS Param1 VARCHAR(32), Param2 INT;
SELECT columns FROM table WHERE
([Param1] IS NULL OR column1 = [Param1])
AND
([Param2] IS NULL OR column2 = [Param2])
Can someone show me how optional parameters are dealt PL/SQL, Thanks
For example:
create or replace procedure optPar(a in number, b in number := null, c number := 999) is
begin
dbms_output.put_line(a || ', ' || b || ', ' || c);
end;
The procedure needs the a parameter:
SQL> exec optPar();
BEGIN optPar(); END;
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'OPTPAR'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
You can omit b, c:
SQL> exec optPar(1);
1, , 999
PL/SQL procedure successfully completed.
SQL> exec optPar(1, 2);
1, 2, 999
PL/SQL procedure successfully completed.
SQL> exec optPar(1, 2, 3);
1, 2, 3
PL/SQL procedure successfully completed.
In this case, explicitly passing parameters is even more important then with no optional parameters, to easily understand which value you are giving to each parameter:
SQL> exec optPar( a=> 1, c=> 3);
1, , 3
PL/SQL procedure successfully completed.

ORACLE use custom tableObject into a function

I want insert a cursor into my custom tableObject, but it is not always found.
My RECORD:
create or replace type "RECORDRANKING" as object
(
-- Attributes
COL1 NUMBER,
COL2 VARCHAR(50),
COL3 NUMBER
-- Member functions and procedures
-- member procedure <ProcedureName>(<Parameter> <Datatype>)
)
This is object table:
CREATE OR REPLACE TYPE "TBRANKING" AS TABLE OF RECORDRANKING;
Now I go into creating a function (into a package):
CREATE OR REPLACE PACKAGE BODY PKLBOTTONI as
FUNCTION testlb
(
p_gapup IN NUMBER,
p_gadown IN NUMBER
)
RETURN SYS_REFCURSOR IS
cursor_ranking SYS_REFCURSOR;
position NUMBER ;
gap_ranking TBRANKING;
gaprecord RECORDRANKING;
upgap NUMBER;
downgap NUMBER;
BEGIN
select * into cursor_ranking from(
select pkranking.getRanking( 100 ) from dual);
LOOP
FETCH cursor_ranking INTO gap_ranking;
EXIT WHEN cursor_ranking%NOTFOUND;
INSERT INTO gap_ranking (COL1,COL2,COL3)
VALUES
(cursor_ranking.C1,
cursor_ranking.C2,
cursor_ranking.C3);
END LOOP;
return gap_ranking;
END;
END PKLBOTTONI;
I always get:
Compilation errors for PACKAGE BODY PKLBOTTONI
Error: PL/SQL: ORA-00942: table or view does not exist
Line: 32
Text: INSERT INTO gap_ranking
In the loop you're both fetching into "gap_ranking" and then trying to "insert into" it again. Insert into a collection is not a valid syntax. Fetch is ok, either in a loop or using bulk collect to fetch multiple records at once.
From your excerpt it doesn't look like you have a physical database table, so the way to do it in PL/SQL would be something like below.
For more help using collections you can check Oracle docs below too:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm
SQL> set serveroutput on
SQL> declare
2 gap_ranking tbranking := tbranking(null); -- initialize
3 begin
4 gap_ranking.delete; -- clear empty record
5 for cur in
6 (select level as i from dual connect by level <= 5)
7 loop
8 -- insert empty
9 gap_ranking.extend;
10 -- attribute values
11 gap_ranking(gap_ranking.last) := recordranking(1000 + cur.i, 'TEST' || cur.i, 10 + cur.i);
12 end loop;
13 -- loop to print - just to illustrate
14 for j in gap_ranking.first .. gap_ranking.last
15 loop
16 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
17 gap_ranking(j).col2 || ',' ||
18 gap_ranking(j).col3);
19
20 end loop;
21 -- same as...
22 for j in 1 .. gap_ranking.count
23 loop
24 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
25 gap_ranking(j).col2 || ',' ||
26 gap_ranking(j).col3);
27
28 end loop;
29 end;
30 /
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
PL/SQL procedure successfully completed
SQL>

How can we define output parameter size in stored procedure?

How can we define output parameter size in stored procedure?
You can't. Of course, you are in control of how much data you put into the OUT parameter in the stored procedure. If you want you can create a sized local variable to hold the data and then assign the value of that variable to the OUT parameter.
The calling program determines the size of the variable that receives the OUT parameter.
Here is a simple package which declares and uses a subtype:
SQL> create or replace package my_pkg as
2 subtype limited_string is varchar2(10);
3 procedure pad_string (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string);
6 end my_pkg;
7 /
Package created.
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 p_out_str := rpad(p_in_str, p_length, 'A');
9 end pad_string;
10 end my_pkg;
11 /
Package body created.
SQL>
However, if we call PAD_STRING() in such a way that the output string exceeds the subtype's precision it still completes successfully. Bother!
SQL> var out_str varchar2(128)
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
PL/SQL procedure successfully completed.
SQL>
SQL> select length(:out_str) from dual
2 /
LENGTH(:OUT_STR)
----------------
12
SQL>
This is annoying but it's the way PL/SQL works so we have to live with it.
The way to resolve the situaton is basically to apply DBC principles and validate our parameters. So, we can assert business rules against the inputs like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 if length(p_in_str) + p_length > 10 then
9 raise_application_error(
10 -20000
11 , 'Returned string cannot be longer than 10 characters!');
12 end if;
13 p_out_str := rpad(p_in_str, p_length, 'A');
14 end pad_string;
15 end my_pkg;
16 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-20000: Returned string cannot be longer than 10 characters!
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
Or we can assert business rules against the output like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 l_str limited_string;
8 begin
9 l_str := rpad(p_in_str, p_length, 'A');
10 p_out_str := l_str;
11 end pad_string;
12 end my_pkg;
13 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
In most scenarios we should do both. This is the polite way to build interfaces, because it means other routines can call our procedures with the confidence that they will return the values they say they will.
You could use a subtype in a package header and type check that in the body...
CREATE OR REPLACE PACKAGE my_test
AS
SUBTYPE my_out IS VARCHAR2( 10 );
PROCEDURE do_something( pv_variable IN OUT my_out );
END;
/
CREATE OR REPLACE PACKAGE BODY my_test
AS
PROCEDURE do_something( pv_variable IN OUT my_out )
IS
lv_variable my_out;
BEGIN
-- Work on a local copy of the variable in question
lv_variable := 'abcdefghijklmnopqrstuvwxyz';
pv_variable := lv_variable;
END do_something;
END;
/
Then when you run this
DECLARE
lv_variable VARCHAR2(30);
BEGIN
my_test.do_something( lv_variable );
DBMS_OUTPUT.PUT_LINE( '['||lv_variable||']');
END;
/
You would get the error
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
Seems to go against the spirit of using an out parameter, but after Tony's comment this was the only thing I could think of to control data within the called code.

Resources