Oracle procedure CALL with package's constant - oracle

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');

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.

Confused about type coercion in function calls (stack frames)

Consider the following example:
create or replace function f(n integer) return integer as
begin
return n;
end;
/
begin
dbms_output.put_line(f(3.8));
end;
/
3.8
PL/SQL procedure successfully completed.
This makes no sense to me. Obviously, PL/SQL simply ignores the integer specification, both on entering the function and on exiting it. Is this simply a bug? Is it a design choice, made deliberately by the language developers?
Here is why I find this confusing. Compare to the following example:
declare
x integer;
begin
x := 3.8;
dbms_output.put_line(x);
end;
/
4
PL/SQL procedure successfully completed.
In this example, the data type specification is complied with. PL/SQL doesn't throw an error, but at least it performs an implicit coercion and it does not violate the data type declared for x - the variable stores the value 4, an integer, not 3.8.
So, how does PL/SQL do the function call thing in the first example? As far as I understand (never having been trained formally in computing), whenever the compiler or interpreter finds a function call it creates a stack frame, with variables for the arguments passed to the function and for the return value to come back from the function. Aren't these variables, when the stack frame is created, supposed to be the same data type as specified in the function declaration? If the stack frame has a field of integer data type for the argument 3.8, how come that is not coerced to 4 before it is even stored in the corresponding variable? And the same thing for the return value: if the function returns 3.8 but the caller expects an integer (and therefore the corresponding variable in the stack frame should be integer), how is it able to accept the return value 3.8?
And, most disturbing - why is this behavior different from the behavior when explicitly declared variables are involved (as in my second example)?
Thank you for sharing your thoughts!
The answer is found in the documentation for Oracle Database (to which your question has absolutely no relation whatsoever).
Firstly, INTEGER in the said database is a synonym for NUMBER(38). Upon assignment to a NUMBER(38) variable x, as in your second example, according to the assignment rules the NUMBER (with arbitrary precision) literal 3.8 is rounded.
In your first example though no assignment happens because IN parameters to PL/SQL subprograms are passed by reference and the same reference (to the NUMBER value 3.8) is returned.

How do I properly call DBMS_WM.GOTOWORKSPACE inside a procedure

I apologize for this relatively simple question but I am new to Oracle and DBMS Workspace Manager. I have a stored procedure that checks the current workspace and if it differs from the workspace being passed in it will execute the DBMS_WM.GOTOWORKSPACE. All the examples show using EXEC while calling DBMS_WM.GOTOWORKSPACE but when I have the following syntax inside TOAD it says its invalid, if I take the EXEC off no errors shown.
Which is correct?
//Toad Does not like this synatx
IF UPPER(l_current_workspace) != UPPER(i_workspace) THEN
EXEC DBMS_WM.GOTOWORKSPACE (i_workspace);
END IF;
OR
//TOAD is fine with this
IF UPPER(l_current_workspace) != UPPER(i_workspace) THEN
DBMS_WM.GOTOWORKSPACE (i_workspace);
END IF;
I would guess that the examples are in a form something like EXEC DBMS_WM.GOTOWORKSPACE (<>);and not between a conditionalIF...END IF;`
EXEC is not a PLSQL reserved word. It is a macro/shortcut in the client rather than the database server, that expands to wrap the statement it precedes into a BEGIN ... END; block.
It is used very widely on the command-line (SQLPlus and SQLcl) and editors like Toad and SQLDeveloper et al. often support it, but it is supported as a monolithic command only (EXEC as a prelude to the block) rather than inside of the block (Nested EXEC is not supported, nor is an EXEC inside of another BEGIN...END; block).
Since it isn't a reserved word, expanding it when it is in another block is a problem. Parsing it becomes ambiguous -- one could define one's own function called EXEC and it would be perfectly valid to run in a PLSQL block. The editor would then need to make an uncertain call about what to do when it encountered the word. The below example would break if EXEC were expanded instead of being recognized as a procedure in the PLSQL block:
CREATE PROCEDURE EXEC(PARAM_1 IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(UTL_LMS.FORMAT_MESSAGE('Param is:[%s]',PARAM_1));
END EXEC;
/
BEGIN
EXEC('VOLTRON');
END;
/
So the second example is correct (presuming it is housed in another BEGIN...END; block), and is universally compatible since it doesn't need to be intercepted and modified client-side.

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.

Nested PIPELINED function is skipped by Oracle

I'll post a trivial example, which actually works, just to get an approximate picture of what I'm trying to achieve:
Here is the 'inner' function which takes the data from some table, called test_tab:
create or replace function test_inner RETURN num_typ PIPELINED
IS
BEGIN
FOR cur in (
SELECT x FROM test_tab
)
LOOP
PIPE ROW(cur.x);
END LOOP;
END;
/
Here is the 'outer' function which uses the result of inner function and transforms them appropriately:
create or replace function test_outer RETURN num_typ PIPELINED
IS
BEGIN
FOR x IN (
SELECT * FROM table(test_inner())
)
LOOP
PIPE ROW(x.column_value * 2);
END LOOP;
END;
/
And here is how I use it:
begin
execute immediate 'insert into test_tab(x) values(1)';
execute immediate 'insert into test_tab(x) values(2)';
execute immediate 'insert into test_tab(x) values(3)';
FOR x IN (
select * from table(test_outer())
) LOOP
DBMS_OUTPUT.put_line(x.column_value);
END LOOP;
end;
/
The problem is that test_inner function seems to be ignored by Oracle. When it is called separately, it 'sees' the data inserted prior to its execution. But when it's called as a part of test_outer it doesn't return any data, or maybe doesn't get called at all.
Like I said, the above example will work. But my case is a little bit more complex, so I can't post it entirely.
The test case you have posted does not reproduce the problem you claim to be seeing. So it is not a test case.
#AlexPoole has provided you with some other ideas. Basically there is nothing further we can do unless you can post a better test case, given that you cannot post the entire things (and we don't want to go through hundreds of lines of somebody else's shonky code - I for one get enough of that debugging my own stuff).
Producing a poor test case has not been a waste of time. At least you can rule out certain things: it's nothing to do with one pipelined function calling another. So it's clearly something in your specific implementation. Perhaps not in the internal function logic, but how they are called, the data they work with or some other part of the infrastructure.
All you can do is analyse the individual parts. Start with the core component, and build out, adding other components until you find the bit which breaks. Yes this is a tedious chore. But from your comments it sounds like you're quite a way through this process already.
I'm marking this CW as it isn't an answer to the question, just an extended comment.
Your both pipelined functions in fact are syntactic wrong: you are missing RETURN
See docs

Resources