Calling procedure - oracle

If we call a procedure inside a procedure like
procedure ter () is cursor XXXXX
another procedure load_ter()
another procedure load_value()
end ter;
Does it calls order wise inside the procedure. or it can be from anywhere

If you run procedure TER and call LOAD_TER, then LOAD_TER will execute until it finishes. Then LOAD_VALUE will be called, executed. Once it finishes, the rest of commands in TER will be run and - finally - TER will terminate as well.

If you execute this procedure:
create or replace procedure demo as
begin
dbms_output.put_line('One');
dbms_output.put_line('Two');
dbms_output.put_line('Three');
end demo;
the output will be (with dbms_output enabled):
One
Two
Three
That is, the instructions within a procedure will always executed in the order you coded them.
I have to wonder what prompted this question.

Related

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

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.

Oracle out parameter not being NULL'd

My understanding is OUT formal parameters should always be defaulted to NULL when the procedure is called.
create or replace package parameter_tests as
procedure callerproc;
end parameter_tests;
/
create or replace package body parameter_tests as
procedure getstring(p_str out varchar) is
begin
if p_str is null then
dbms_output.put_line('parameter null');
else
dbms_output.put_line('parameter NOT null');
end if;
p_str :='zz';
end getstring;
procedure getcursor(p_out out sys_refcursor) is
begin
if p_out%isopen then
dbms_output.put_line('cursor open');
else
dbms_output.put_line('cursor closed');
end if;
open p_out for
select *
from dual;
end getcursor;
procedure callerproc is
lv_cursor sys_refcursor;
lv_string varchar2(2) := null;
begin
for i in 1..2 loop
getstring(lv_string);
getcursor(lv_cursor);
end loop;
end callerproc;
end parameter_tests;
/
set serveroutput on
begin
parameter_tests.CALLERPROC;
end;
/
parameter_tests.getstring would expect to output "Parameter null" both times around the callerproc loop. And when you run the code that's exactly what happens.
However the output from parameter_tests.getcursor indicates the reference cursor is still open the second time around the loop.
If the formal parameter p_out was being nulled when getcursor was called, we'd expect it to close the reference cursor. Instead it passes the open reference cursor and it's actually the OPEN FOR that tidies up the open reference cursor (and prevents us from hitting the maximum open cursors if you loop hundreds of times).
If I manually null the reference cursor between calls it does behave like we would expect.
Can anyone tell me why reference cursors are being handled as a special case when they are OUT parameters? And also what other types are handled differently?
The database version is 11.2.0.2.0.
According to the documentation:
When declaring a cursor variable as the formal parameter of a subprogram:
If the subprogram opens or assigns a value to the cursor variable, then the parameter mode must be IN OUT.
If the subprogram only fetches from, or closes, the cursor variable, then the parameter mode can be either IN or IN OUT.
The cursor parameter seems to be treated as if it had been declared as IN OUT, even though you only actually declare it as OUT. The behaviour is what you would expect to see from IN OUT; you can even fetch the cursor in the second call, and see the dummy value from dual.
It sort of makes sense given the nature of the ref cursor as a pointer, but you would think that rule would be enforced by the compiler erroring if only OUT was specified (and it isn't even reported as a warning). The example in the docs works fine with just OUT too, incidentally.
So really this looks like a compiler bug since it doesn't report the incorrect parameter direction; but also (more tenuously!) a bug in your code because it isn't declared as IN OUT. And, possibly, a further bug because you don't explicitly close the cursor - which does appear to 'fix' the issue too, sort of:
procedure callerproc is
lv_cursor sys_refcursor;
lv_string varchar2(20) := null;
begin
for i in 1..2 loop
getstring(lv_string);
getcursor(lv_cursor);
close lv_cursor;
end loop;
end callerproc;
which is probably more correct than assigning null to the cursor variable, which you mentioned also works.

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