Oracle PL/SQL Return in Blocks - oracle

Just a question since I can't seem to find the answer somewhere else.
So I got an PL/SQL function which contains a nested block, and within the 2nd level block it got a return value of 1. Does this mean that it will not proceed to the next block and return the value 1 or it will only terminate the current block?
Thanks!
Sample structure for illustration:
FUNCTION EXAMPLE ( sample_variable VARCHAR2)
RETURN NUMBER
IS
BEGIN
BEGIN
/*CODES HERE*/
EXCEPTION
WHEN OTHERS THEN
RETURN 1; //HERE IS THE QUESTION. WHEN I GOT HERE IN RETURN DOES IT TERMINATE THE WHOLE
//FUNCTION AND RETURN 1 OR IT WILL STILL CONTINUE TO BLOCK 2*/
END;
BEGIN /*BLOCK 2*/
/*OTHER CODES HERE*/
RETURN 2;
END;
END
END EXAMPLE;

Terminates the whole function.
From oracle docs:
In a function, the RETURN statement assigns a specified value to the
function identifier and returns control to the invoker, where
execution resumes immediately after the invocation.

Related

Assigning procedures to variables and calling them in Pascal

I have a small terminal program which displays a menu. I want to have a function which takes the user's input and an array of procedure names and be able to call the procedure the user chose. I know that I could do that using if or case statements in the main program, but I want to make a unit with the above procedure and a couple of other menu functions, and if this was possible, I could make it call arbitrary procedures. Here's more or less how I would like it to work (I know it's wrong, but so you get a general idea).
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: int; procsy: procs);
begin
writeln(procsy[ord(inp)]); {And call it here.}
end;
var procsx : procs;
begin
procsx[0] := readkey; {I would like to somehow store the procedure here.}
procsx[1] := clrscr;
call_procs(0, procsx);
end.
Is there any way to do something like this? Thank you in advance.
There are a few things wrong with your original code which are not cited in your answer.
You have an array of procedure but you are calling writeln with these procedure calls as arguments as if they were function, which they are not.
readkey is a function, not a procedure, so its type doesn't match the element type of your array
Your assignment of the procedures to the array need to use # to reference the procedure pointer and not actually call the procedure
Not sure what compiler or options you're using, but int isn't the standard Pascal integer type, rather integer is.
As a niggle, since you're already using the integer index of the array, you don't need to use ord.
So the minimal changes to your code to make it basically work would be:
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: integer; procsy: procs);
begin
procsy[inp]; { call the procedure here - removed 'ord' since it's superfluous }
end;
var procsx : procs;
begin
{ procsx[0] := readkey; {- 'readkey' is a function and won't work here }
procsx[1] := #clrscr;
call_procs(1, procsx);
end.
You can create an array of functions that return char which matches the type for readkey:
program menu;
uses crt;
type procs = array [0..1] of function: char;
procedure call_procs(inp: integer; procsy: procs);
begin
writeln(procsy[inp]); { call the function; write out the returned char }
end;
function foo: char;
begin
foo := 'X';
end;
var procsx : procs;
begin
procsx[0] := #readkey;
procsx[1] := #foo;
call_procs(0, procsx);
call_procs(1, procsx);
end.
I figured out how to do this. One can use pointers to a procedure, then create an array of those pointers, and pass them to the procedure I wanted to use. Also, for some reason, it doesn't seem to work with the functions that come with Pascal (such as readkey or clrscr). For this example, one could do this:
program menu;
type
Tprocptr = procedure; {This creates a pointer to procedures.}
Tprocarray = array of Tprocptr;
procedure writeHi;
begin
writeln('Hi!');
end;
procedure writeHello;
begin
writeln('Hello!');
end;
procedure call_proc(inp: integer; procsy: Tprocarray);
{This now calls functions like one would expect.}
begin
procsy[ord(inp)];
end;
var
proclist : Tprocarray;
begin
setlength(proclist, 2);
proclist[0] := #writeHi; {The '#' creates a pointer to those procedures.}
proclist[1] := #writeHello;
call_proc(0, proclist);
end.
This works as expected, calling (in this case) the procedure writeHi, so if you run this program it will output Hi! to the terminal. If you change call_proc(0,proclist) to call_proc(1, proclist), it will call writeHello instead.

PL/SQL functions with one parameter

I was wondering if you could help.
I'm at my wits end with this.
I want to create a function that will take one parameter.
Here is my code. I keep getting the error when run:
Error(3,19): PLS-00103: Encountered the symbol ";" when expecting one of thefollowing:
begin function pragma procedure subtype type current cursor delete exists prior external language
Code
set serveroutput on;
CREATE OR REPLACE FUNCTION CheckBookType (
p_book_type TITLES.CATEGORY
) RETURN BOOLAN IS;
BEGIN
IF (p_book_type = 'business') THEN
return true;
ELSE
return false;
END IF;
RETURN v_ReturnValue;
END CheckBookType;
Least verbose solution:
CREATE OR REPLACE FUNCTION CheckBookType (
p_book_type IN TITLES.CATEGORY%type
) RETURN BOOLEAN
IS
BEGIN
return p_book_type = 'business';
END CheckBookType;
Let's see the issues line by line in your code :
set serveroutput on;
It doesn't make any harm, but I don't see how it helps or is required in the context.
p_book_type TITLES.CATEGORY is wrong, since you need to specify that it is a constrained data type using the %TYPE attribute. The correct way is
p_book_type TITLES.CATEGORY%TYPE
Already mentioned above by others, the datatype of return value should be BOOLEAN. and you don't have to put semi-colon after "IS".
The variable to return the boolean value is NOT declared.
v_ReturnValue BOOLEAN;
Remember, using BOOLEAN data type as return will NOT let you use the function in SQL. Since, boolean is NOT a SQL data type. Boolean is PL/SQL data type. You could only use the function in PL/SQL and NOT in SQL.
Your function would look like :
Quite verbose.
CREATE OR replace FUNCTION Checkbooktype (p_book_type titles.category%TYPE)
RETURN BOOLEAN
IS
v_returnvalue BOOLEAN;
BEGIN
IF ( p_book_type = 'business' ) THEN
v_returnvalue:= TRUE;
ELSE
v_returnvalue:= FALSE;
END IF;
RETURN v_returnvalue;
END checkbooktype;
/
Or, without and ELSE, taking return default as FALSE.
Less verbose.
CREATE OR replace FUNCTION Checkbooktype (p_book_type titles.category%TYPE)
RETURN BOOLEAN
IS
v_returnvalue BOOLEAN;
BEGIN
v_returnvalue:= FALSE;
IF ( p_book_type = 'business' ) THEN
v_returnvalue:= TRUE;
END IF;
RETURN v_returnvalue;
END checkbooktype;
/
#APC's solution is least verbose.

PL/SQL How to call a function without getting returned object

I have a function in PL/SQL:
FUNCTION do_something
RETURN BOOLEAN
IS
...
BEGIN
...
END
This function can be called as such:
DECLARE
answer BOOLEAN;
BEGIN
answer := do_something();
END
Now, there are cases, where I don't need the returned boolean. If I don't want to get it and just write:
do_something();
I get PLS-00306 (wrong number of types of arguments in call to DO_SOMETHING)
Is there a way to call it without declaring and setting a new boolean, which I will never use in this case?
Very simple: create a procedure which covers this function
PROCEDURE do_something
IS
dummy boolean;
BEGIN
dummy := do_something();
END;
Sorry, but this is the only way in PL/SQL. This language is very strict in definitions of procedure and function and you cannot make a function call without handling the result. But you can make a procedure as it is shown in example above.
It will define automatically where to choose the function and where the procedure.
EDIT
As far as there are people who do not trust me (sometimes I really tell bad things so doubts are allowed :) ) this is the test:
declare
myresult boolean;
function do_something return boolean
is
begin
return true;
end;
procedure do_something
is
dummy boolean;
begin
dummy := do_something();
end;
begin
myresult := do_something();
do_something();
end;
works well.
Here is a useful alternative to assigning a dummy variable:
DECLARE
FUNCTION f_yes RETURN boolean IS
BEGIN
return true;
END f_yes;
BEGIN
IF f_yes THEN
null;
END IF;
END;
This is slightly more helpful when there are functions of different return types you want to call without using their results. It's also helpful for writing empty IF statements, which could be useful to to simplify code by eliminating possibilities for the real condition you want to match in a later ELSIF.
I'm in the same situation and the shortest solution I've come up is wrapping it in an IF statement:
if do_something then
null;
end if;

Win64 exception stack walking not displaying entries

While reading up on Win64 structured exception tracing (from Programming against the x64 exception handling support, part 7: Putting it all together, or building a stack walk routine), I converted the code StackWalk64.cpp.
procedure DumpExceptionStack();
var
LContext : CONTEXT;
LUnwindHistoryTable : _UNWIND_HISTORY_TABLE;
LRuntimeFunction : Pointer;
LImageBase : ULONGLONG;
HandlerData : Pointer;
EstablisherFrame : ULONG64;
NvContext : KNONVOLATILE_CONTEXT_POINTERS;
LLineNumber : integer;
LModuleName : UnicodeString;
LPublicAddr : pointer;
LPublicName : UnicodeString;
LUnitName : UnicodeString;
begin
//
// First, we'll get the caller's context.
//
RtlCaptureContext(LContext);
//
// Initialize the (optional) unwind history table.
//
LUnwindHistoryTable := Default(_UNWIND_HISTORY_TABLE);
// LUnwindHistoryTable.Unwind := True;
//
// This unwind loop intentionally skips the first call frame, as it shall
// correspond to the call to StackTrace64, which we aren't interested in.
//
repeat
//
// Try to look up unwind metadata for the current function.
//
LRuntimeFunction := RtlLookupFunctionEntry(LContext.Rip,
LImageBase,
LUnwindHistoryTable);
NvContext := Default(KNONVOLATILE_CONTEXT_POINTERS);
if not Assigned(LRuntimeFunction) then
begin
//
// If we don't have a RUNTIME_FUNCTION, then we've encountered
// a leaf function. Adjust the stack approprately.
//
//LContext.Rip := (ULONG64)(*(PULONG64)Context.Rsp);
LContext.Rip := ULONG64(Pointer(LContext.Rsp)^);
LContext.Rsp := LContext.Rsp + 8;
end
else
begin
//
// Otherwise, call upon RtlVirtualUnwind to execute the unwind for
// us.
//
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
LImageBase,
LContext.Rip,
LRuntimeFunction,
LContext,
HandlerData,
EstablisherFrame,
NvContext);
end;
//
// If we reach an RIP of zero, this means that we've walked off the end
// of the call stack and are done.
//
if LContext.Rip = 0 then
Break;
//
// Display the context. Note that we don't bother showing the XMM
// context, although we have the nonvolatile portion of it.
//
if madMapFile.GetMapFileInfos(Pointer(LContext.Rip),
LModuleName,
LUnitName,
LPublicName,
LPublicAddr,
LLineNumber) then
begin
Writeln(Format('%p %s.%s %d', [Pointer(LContext.Rip), LUnitName, LPublicName, LLineNumber{, LSEHType}]));
end;
until LContext.Rip = 0;
end;
Then I call it with the following:
procedure Main();
begin
try
try
try
try
DumpExceptionStack();
finally
//
end;
except
on E : Exception do
raise
end;
except
on E : Exception do
raise
end;
except
on E : Exception do
raise
end;
end;
When I run the application (just a console application), I only get one entry for Main but I was expecting there to be four (three nested exceptions and one finally).
Could it be that I have mis-interpretted and that DumpExceptionStack will only give the result that I am interested in when an exception is thrown? If this is so, what would the required changes be to get all the exception stacks (if it possible) - ie. have four outputs for Main ?
The x64 exception model is table based, in contrast to the stack based x86 model. Which means that exception stacks do not exist. In any case, I've never seen a stalk walk routine that attempts to include exception and finally blocks. This one is no different. It walks the function call stack.
The exception flow within a single function is controlled by the scope tables. In your function, if your code raised an exception at the point at which is calls DumpExceptionStack, then multiple scope table entries match the exception location. The exception is handled by the innermost matching scope. The distance between the scope's begin and end addresses can be used to infer which scope is the innermost. If that innermost scope does not handle the exception, or re-raises it, then the next most innermost scope is asked to handle it. And so on until all the matching scopes for the function are exhausted.

PL/SQL to initialize outbound variables?

I am writing a C++ program using OCI to call the stored procedure. In my PL/SQL stored procedure test, if not initializing an outbound variable, I might get "fetched column value is NULL" error because in case of foo != 0, bar is NULL. So in the first line, I initialize bar first. Is this the right way to handle outbound variable?
FUNCTION function1(
foo IN INTEGER,
bar OUT VARCHAR2
) RETURN INTEGER
IS
ret INTEGER;
BEGIN
bar := ' '; -- do I need to initialize this variable?
IF foo = 0 THEN
ret := 0;
bar := 'a';
ELSE
ret := 1;
END IF;
RETURN ret;
END function1;
If you don't set BAR in your program then the variable will be initialized to the default value of its type (NULL in this case, and for any type except for a record type with a non-NULL default value). That's just basic logic.
So your choices are:
Accept the default value for the type of your variable.
Set BAR to the appropriate value in each branch of the IF statement.
Your posted code uses the first option. In the simple logic you present that is the approach I would take. If the internals were more complicated - an IF or a CASE with many branches - I would choose to have every branch explicitly set the value, because that would probably make the code's intent clearer.

Resources