PL/SQL, how to put common code lines of packages in a shared package - oracle

I have several packages which some of their Specification and body lines are the same, so I would like to put all the same codes in a Common package and share that package in all.
For procedures body, it was not so difficualt as I defined a Main procedure in my Common package and I used it in other packages, but how can I do the same for Specifications ?
suppose I have some common variable or constant variable:
CNST_S_DATA_MINIMA constant varchar2 (10) := '1900-01-01';
CNST_D_DATA_MINIMA constant date := To_date (CNST_S_DATA_MINIMA, CNST_S_FORMATO_DATE);
ERR__VOID_PARAMETER EXCEPTION;

Simply put them into the package specification (not body) of a package that is used as the container for such global constants.

Related

Can we declare and use a cursor in a package specification without a body?

It's said in database PL/SQL language reference in topic 10.1 What is a package? that:
If the public items include cursors or subprograms, then the package must also have a body. The body must define queries for public cursors and code for public subprograms.
I've tested cursor package spec without body and it worked fine (in DB version 19c):
create or replace package some_pak is
cursor c is select * from employees where employee_id <102;
end;
begin
for i in some_pak.c
loop
dbms_output.put_line(i.employee_id|| ' '||i.first_name||' '|| i.salary);
end loop;
end;
Result:
100 Steven 24000
101 Neena 17000
What am I doing or understanding wrong?
I am preparing for 1z0-149 exam and want to know accurate information.
It is a bit misleading, but if you take the previous paragraph from the documentation into account:
A package always has a specification, which declares the public items that can be referenced from outside the package.
... and stress the declares in that, then it sort of makes sense; if you only declare a cursor, rather than declaring and defining as your example does, then you do need a body too. For example, your specification could do:
create or replace package some_pak is
cursor c return dual%rowtype;
end;
/
But if you tried to reference that cursor you'd get "ORA-04067: not executed, package body ... does not exist".
You would then need to define the cursor in the package body, e.g.:
create or replace package body some_pak is
cursor c return dual%rowtype is
select * from dual;
end;
/
Notice that you need to declare the return type, in both the specification and body. In your original version that isn't necessary, but here the declaration still has to tell callers the structure of the data the cursor will return. That is the API contract, if you like.
Declaring and defining the cursor separately means you can redefine the cursor by recompiling just the package body (as long as the projection remains the same, of course), avoiding invalidating anything that refers to it - as would happen if you recompiled the specification.
db<>fiddle
Splitting the declaration and definition would also allow you to hide (albeit not very securely) the actual cursor query by wrapping the package body, without wrapping the specification. Whether that would ever be useful is another matter, but thought it might be worth mentioning anyway.

Oracle procedure CALL with package's constant

Let's assume simple scenario like below:
CALL DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => DBMS_XMLSCHEMA.DELETE_INVALIDATE);
-- ORA-06553: PLS-221: 'DELETE_INVALIDATE' is not a procedure or is undefined
Meanwhile when providing integer value or running inside PL/SQL block the error does not occur:
-- 1)
CALL DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => 2);
-- 2)
BEGIN
DBMS_XMLSCHEMA.DELETESCHEMA(
schemaurl => 'non_existing_schemaurl',
delete_option => DBMS_XMLSCHEMA.DELETE_INVALIDATE);
END;
/
db<>fiddle demo
Is it possible to use DBMS_XMLSCHEMA.<constant> combined with CALL(searching for credible/official source)?
According to the SQL Language Reference:
Use the CALL statement to execute a routine (a standalone procedure or
function, or a procedure or function defined within a type or package)
from within SQL.
The key is the last two words - within SQL. It's easy to think of CALL as similar to the SQL*Plus command EXEC, but CALL runs in a SQL context while EXEC runs in a PL/SQL context.
PL/SQL package variables cannot be used in SQL statements (except for PL/SQL WITH functions, but that feature doesn't work in this context). Unfortunately I can't find an authoritative source for that claim (which is why this is a Wiki answer; hopefully someone else can add that source and then remove this sentence.)
The way you are trying to call is possible only in any named or unnamed block of pl/sql.
To call it independently, you need to pass the arguments only.
CALL DBMS_XMLSCHEMA.DELETESCHEMA('none_existing_schemaurl','DELETE_INVALIDATE');

PL/SQL - Call new instance of package procedure

I have package that uses global variables.
From one of the procedures i need to call "new instance" od another procedure (of the same package). I need this second procedure to use it's own version of global variables and when it's done i need first procedure to use variables as they were before.
I did a quick and dirty fix, at the beginning of second procedure i create backup variables and put global variables values in it and then setting global variables to NULL and at the end i put backup values back to global variables.
But i hope there is better solution than this one, a way to start new instance of that procedure/package
EDIT
Detailed scenario is like this:
Package has multiple procedures and functions that use variables defined at package level. I call firs procedure of the package and that procedure calls everything that is needed for current action. In some cases from second procedure i need to start new task (action) that has to be executed. In that case i would like to start again first procedure in package, but for it to use its own global variables (not to mess with original ones) because original procedure is not yet finished and still needs it's variables
Some of package global variables are custom type (table of custom_type to be exact) so it's creating additional problem for me.
Hope it's clear now what problem i have and what i'm trying to do
So it's something like this
Package 1
global_var 1
global_var 2
global_var 3
Procedure 1
some code
call to function 1
some code
call to procedure 2
and so on...
Procedure 2
some code
call function 3
some code
call to procedure 1 (start new process)
some code
call procedure 3
and so on....
Procedure 3
Procedure 4
Function 1
Function 2
Function 3
Function 4
And variables are like this
type type1 is record (x NUMBER, y VARCHAR2(1024), z NUMBER);
type type2 is table of type1;
global_var_1 NUMBER;
global_var_2 type2;
...
After reading your entire post (with the additional description), I came to the conclusion that your problem starts with a wrongly conceived view of the required process. In fact (and lending words from other programming languages) you need your package to spawn new threads with their own data context.
I see two possible solutions (in Oracle):
Write your package in JAVA (here I'm assuming that this option does allow the creation of threads, but never checked it),
Do not use GLOBAL VARIABLES.
For the first option I have no much to say except that it may be worth for you to investigate further.
As for the second option, you can (quite easily in fact):
Define a type a package level that will contain all the variables that you are currently using at global level and:
Add to all your functions in the package a parameter of the new type (I would put it as the last one) with default value NULL. Wherever needed, the definition of the new parameter can be set as IN OUT (for the case that one or more of the fields within the structure is updated within a function and the update needs to "bubble-up" to the invoking code).
Now, let's analyze the two scenarios (first invocation of FIRST procedure and second invocation from within the package):
FIRST INVOCATION (from outside the package): The new parameter will be empty (NULL) and the procedure will assign whatever values are needed to cascade throughout additional invocations of other functions/procedures;
SECOND INVOCATION (from within the package): can include a non-NULL value for the new parameter (depending on your needs); the code will then be able to prepare a new set of values (for a new variable of the type described above) which will then be cascaded through the additional invocations of other functions and procedures.
This method achieves exactly what you need while (as far as I can imagine) the required code changes would be minimal.
Hope this addresses the question you posted and the proposed approach serves you well.

Overloading oracle procedure with DATE and VARCHAR2

I have the following code that doesn't work. It compiles, but when called with sysdate as the parm_value parameter it throws PLS-00307: too many declarations of 'P_UPSERT_SDE_DATA' match this call If I comment out the varchar2 entry, the overload works as expected with just date and number datatypes.
What is the best way to go about what I'm trying to do, which is accept parameters differing only in parm_values datatype(specifically date and varchar2)?
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value VARCHAR2);
--
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value NUMBER);
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value DATE);
You can see this if one of the others arguments you pass is being implicitly converted; from the call you posted I suspect var_FRB‌​GRNT_CODE is a different type and is being converted; e.g. that variable is a number and GORSDAV.GORSDAV_PK_PARENTTAB is a string.
From the documentation:
When trying to determine which subprogram was invoked, if the PL/SQL compiler implicitly converts one parameter to a matching type, then the compiler looks for other parameters that it can implicitly convert to matching types. If there is more than one match, then compile-time error PLS-00307 occurs, as in Example 8-34.
Implicit conversion of one of the other arguments makes it look for potential conversions of the others; it's only then that it sees the date and varchar2 versions, which can be implicitly converted to each other. If all of the arguments are of the same type as the table columns used for the formal parameter declarations then it won't look for implicit conversions, and won't be confused by the date/varchar2 versions.
Whenever I get this error, it seems to be that there's a mismatch between the spec declaration and the body declaration somewhere. Make sure each of your spec declarations match your body declarations exactly.

Oracle PL/SQL package compilation with interdependent procedure

If I have procedure Proc1 and another Procedure Proc2 where proc1 depends on proc2 and proc2 depends on proc1 . I need to compile package pkg1 with both these procedures .
Oracile 9i.
How can I achieve it? any particular keyword is required ?
I presume these are both private procedures, that is, neither appears in the package specification.
If both procedures appeared in the spec there would be no problem, because the body compiles against the public declaration.
Likewise if one procedure appeared in the spec then again there would be no problem: simply write the private procedure in the body before the public procedure.
But if both procedures are private, then you need to use forward declaration. This simply means declaring the procedures' signatures at the top of the package. Exactly like putting them in the spec, only private. Here is a spec ...
create or replace package pkg as
procedure main (n0 in out number);
end;
/
... and body ...
create or replace package body pkg as
-- forward declarations
procedure p1 (n1 in out number);
procedure p2 (n2 in out number);
-- actual declarations
procedure p1 (n1 in out number)
is
begin
dbms_output.put_line('P1='||n1);
if n1 < 3 then
p2(n1);
end if;
end p1;
procedure p2 (n2 in out number)
is
begin
dbms_output.put_line('P2='||n2);
n2 := n2+1;
if n2 < 3 then
p1(n2);
end if;
end p2;
procedure main (n0 in out number)
is
begin
if n0 < 0 then
p1(n0);
else
p2(n0);
end if;
end main;
end pkg;
/
"I need to have the interdependent procedures"
You really should try to figure out a way to avoid cyclic dependencies. It is a very bad programming practice. It is hard to understand how the procedures interrelate, and we have to manage the recursion and ensure that both procedures will definitely exit eventually without calling the other. Remember, this is not just a problem you have: it's a legacy you will pass one to future maintainers of your code.
You can break the dependency chain by using packages and compiling all your package specs prior to compiling package bodies. The specs will all compile without regard to dependencies and then when the bodies are compiled they can do so in any order as they will reference the already compiled specifications. Typically, you'll have package specs stored with a *.pks suffix and bodies with *.pkb suffix and some sort of build script with compiles them. Like so
/* Master Package Build Script */
-- Specs
#package_a.pks
#package_b.pks
#package_c.pks
-- Bodies
#package_a.pkb
#package_b.pkb
#package_c.pkb
If both procedures are in the specification, simply compiling the package should work:
alter package your_package compile;
This assumes that your package is valid, of course.
If either (or both) procedure is not in the specification, the best thing to do is list it as a forward declaration at the beginning of the package body.

Resources