Oracle SQL PL/SQL: use return value of function as input for another function? - oracle

it's probably only a misunderstanding of the different types of variables that exist in Oracle SQL and PL/SQL, but how can I use the return value of a PL/SQL function as input for another PL/SQL function inside a SQL script without having to manually set it as value of a DEFINE variable?
Here is the code (being run inside a SQL script inside Oracle SQL Developer):
-- some INSERTS/UPDATES/SELECTS ...
DEFINE in = 'somevalue';
VAR return1 NUMBER;
EXECUTE :return1 := someschema.somepackage.somefunction(in);
PRINT return1;
-- reasonable return value gets printed out
VAR return2 NUMBER;
EXECUTE :return2 := someschema.somefunction(return1);
-- ^
-- this does not work ----------------------+
-- (neither does ":return1")
DEFINE in2 = <manually enter value of "return1">
EXECUTE :return2 := someschema.somefunction(in2);
-- ^
-- this works ------------------------------+
-- some INSERTS/UPDATES/SELECTS ...
Thank's in advance.

DEFINE and EXECUTE would work as expected in SQL*Plus. To execute your entire code in SQL Developer or as a script from a client, I would suggest you:
Use an anonymous block.
DECLARE a variable for the OUT parameter of procedure, and another variable to store the return value of the function.
In the BEGIN-END block, call the procedure.
And use the same varible to store the return value of the function.
For example,
DECLARE
o_var ;
f_var ;
BEGIN
-- call the procedure
package.procedure(o_var);
--call the function
SELECT package.function(o_var) INTO f_var FROM DUAL;
-- do something
END;
/

Below solution works for me.
DECLARE
parameter_var ;
assignment_var ;
BEGIN
--call the function and assign it to assignment_var
assignment_var:=function_xyz(parameter_var);
-- use assignment_var for further computation
END;
/
Hope it helps others.

Related

A syntax for custom lazy-evaluation/short-circuiting of function parameters

Oracle defines several structures that make use of what looks like lazy evaluation but what's actually short-circuiting.
For example:
x := case when 1 = 2 then count_all_prime_numbers_below(100000000)
else 2*2
end;
The function count_all(...) will never be called.
However, what I'm more interested in is the syntax that looks like regular function call:
x := coalesce(null, 42, hundreth_digit_of_pi());
Hundreth_digit_of_pi() will not be called since coalesce is not a regular function, but a syntax sugar that looks like one - for regular ones parameters get evaluated when the function is called.
The question is:
is it possible do define in plsql a custom procedure/function that would behave in the same way?
If you're not convinced I'll give an example when that could be useful:
We use ''framework'' for logging.
To trace something you call a procedure:
trace_something('A text to be saved somewhere');
Trace_something is 'pragma autonomous transaction' procedure that does the following steps:
1. See, if any tracing methods (for example file / db table) are enabled
2. For every enabled method use that method to save parameter somewhere.
3. If it was saved to DB, commit.
A problem occurs when building the actual string to be traced might take noticable amount of time, and we wouldn't want to have to spend it, if tracing isn't even enabled in the first place.
The objective would be, in pseudocode:
procedure lazily_trace_something(some_text lazily_eval_type) {
if do_i_have_to_trace() = TRUE then
trace_something(evaluate(some_text));
else
NULL; -- in which case, some_text doesn't get evaluated
end if;
}
/*
*/
lazily_trace_something(first_50_paragraphs_of_lorem_ipsum(a_rowtype_variable));
Is it possible to be done in plsql?
Lazy evaluation can be (partially) implemented using ref cursors, conditional compilation, or execute immediate. The ANYDATA type can be used to pass generic data.
Ref Cursor
Ref cursors can be opened with a static SQL statement, passed as arguments, and will not execute until needed.
While this literally answers your question about lazy evaluation I'm not sure if it's truly practical. This isn't the intended use of ref cursors. And it may not be convenient to have to add SQL to everything.
First, to prove that the slow function is running, create a function that simply sleeps for a few seconds:
grant execute on sys.dbms_lock to <your_user>;
create or replace function sleep(seconds number) return number is
begin
dbms_lock.sleep(seconds);
return 1;
end;
/
Create a function to determine whether evaltuation is necessary:
create or replace function do_i_have_to_trace return boolean is
begin
return true;
end;
/
This function may perform the work by executing the SQL statement. The SQL statement must return something, even though you may not want a return value.
create or replace procedure trace_something(p_cursor sys_refcursor) is
v_dummy varchar2(1);
begin
if do_i_have_to_trace then
fetch p_cursor into v_dummy;
end if;
end;
/
Now create the procedure that will always call trace but will not necessarily spend time evaluating the arguments.
create or replace procedure lazily_trace_something(some_number in number) is
v_cursor sys_refcursor;
begin
open v_cursor for select sleep(some_number) from dual;
trace_something(v_cursor);
end;
/
By default it's doing the work and is slow:
--Takes 2 seconds to run:
begin
lazily_trace_something(2);
end;
/
But when you change DO_I_HAVE_TO_TRACE to return false the procedure is fast, even though it's passing a slow argument.
create or replace function do_i_have_to_trace return boolean is
begin
return false;
end;
/
--Runs in 0 seconds.
begin
lazily_trace_something(2);
end;
/
Other Options
Conditional compilation is more traditionally used to enable or disable instrumentation. For example:
create or replace package constants is
c_is_trace_enabled constant boolean := false;
end;
/
declare
v_dummy number;
begin
$if constants.c_is_trace_enabled $then
v_dummy := sleep(1);
This line of code does not even need to be valid!
(Until you change the constant anyway)
$else
null;
$end
end;
/
You may also want to re-consider dynamic SQL. Programming style and some syntactic sugar can make a big difference here. In short, the alternative quote syntax and simple templates can make dynamic SQL much more readable. For more details see my post here.
Passing Generic Data
The ANY types can be use to store and pass any imaginable data type. Unfortunately there's no native data type for each row type. You'll need to create a TYPE for each table. Those custom types are very simple so that step can be automated if necessary.
create table some_table(a number, b number);
create or replace type some_table_type is object(a number, b number);
declare
a_rowtype_variable some_table_type;
v_anydata anydata;
v_cursor sys_refcursor;
begin
a_rowtype_variable := some_table_type(1,2);
v_anydata := anydata.ConvertObject(a_rowtype_variable);
open v_cursor for select v_anydata from dual;
trace_something(v_cursor);
end;
/

Oracle Update and select in a single query

I'm working with a vendor's database, and I need to increment a value and return it. I have the right command, it updates the table, but the output in oracle developer just says "PL/SQL procedure successfully completed." How do I return NewReceiptNumber?
DECLARE
NewReceiptNumber int;
BEGIN
UPDATE SYSCODES
SET (VAR_VALUE) = VAR_VALUE+1
WHERE VAR_NAME='LAST_RCPT'
RETURNING VAR_VALUE INTO NewReceiptNumber;
END;
You are just depicting an anonymous block there, to VIEW the value of NewReceiptNumber in that code you'll have to use:
dbms_output.put_line(NewReceiptNumber).
as in:
DECLARE
NewReceiptNumber int;
BEGIN
UPDATE SYSCODES
SET (VAR_VALUE) = VAR_VALUE+1
WHERE VAR_NAME='LAST_RCPT'
RETURNING VAR_VALUE INTO NewReceiptNumber;
dbms_output.put_line(NewReceiptNumber);
END;
But if your intention is to actually return this value to another procedure or a call from your front-end, you'll have to declare a stored procedure and within that declaration indicate your output parameter, something like:
CREATE OR REPLACE PROCEDURE my_proc(newreceiptnumber out INT) AS
BEGIN
UPDATE syscodes
SET (var_value) = var_value+1
WHERE var_name='LAST_RCPT'
RETURNING var_value INTO newreceiptnumber;
END my_proc;
And of course it'll be a more robust solution if you send your updated values as parameters as well, this is just the minimal approach.

How to retrieve values post-execution of PL/SQL stored procedure?

Im very new to oracle database stuff. There is a PL/SQL proceedure which someone else wrote and is stored on the database I am accessing. I want my program to execute it and retrieve the result. Execution is working. I cannot retrieve the result however. Obviously I am not doing it right, but I cannot find the right way in the documentation.
Here is the gist of the stored procedure (with extraneous lines removed)
procedure ISDRAWINGVALID(DWGNO_IN in VARCHAR2) is
valid BOOLEAN;
begin
-- do some stuff to see if the drawing is valid
IF <some stuff> THEN
valid := TRUE;
ELSE
valid := FALSE;
END IF;
END ISDRAWINGVALID;
My program issues the following commands to the database to execute and retrieve the return.
BEGIN ISDRAWINGVALID( <drawingnumber> ); END;
SELECT ISDRAWINGVALID.valid FROM DUAL;
The first line works fine, the proceedure executes and has the desired effect.
The second line returns an error, invalid identifier "ISDRAWINGVALID.valid"
Clearly i am not using the right way to retrieve the value. Can someone please clue me in?
thanks
As you present the problem, there is no way to get the result.
If you can get the procedure as a function instead, you can call it directly in the select statement.
Otherwise you would have to take a long detour to solve it, involving a result table or a pl/sql package with a result function and a package variable.
The procedure you have there has been made to be called from other pl/sql code - not in a select query.
EDIT
I think I might be wrong after all.
In Java you can create a prepared statement with a call, and pick up the return value directly as a result-set.
Check this out and come back with the result: http://archive.oreilly.com/pub/a/onjava/2003/08/13/stored_procedures.html?page=2
Sorry if you are not using Java, I was not able to see what you are using.
Use a function and return a NUMBER to be used in SQL:
CREATE OR REPLACE
FUNCTION ISDRAWINGVALID(DWGNO_IN in VARCHAR2) RETURN NUMBER
IS
valid NUMBER;
BEGIN
IF <some stuff> THEN
valid := 1;
ELSE
valid := 0;
END IF;
RETURN valid;
END ISDRAWINGVALID;
Use from PL/SQL:
DECLARE
valid NUMBER;
BEGIN
valid := ISDRAWINGVALID( <drawingnumber> );
END;
/
Use from SQL:
SELECT ISDRAWINGVALID( <drawingnumber> ) FROM DUAL;

How to test PL/SQL procs involving arrays from Toad?

I have a need to test a stored procedure involving arrays that are written in PL/SQL. I have no way of seeing the PL/SQL contents (shop rules) but need to call the proc. I'd like to know how to call the following proc that invovles 3 arrays of integers directly from toad. The method signature looks like this.
Procedure persistChanges(myKey in NUMBER, arrayOfIntsFirst numberTableType, arrayOfIntsSecond numberTableType, arrayOfIntsThird numberTableType).
How can I call a PL/SQL proc in TOAD where I can hard-code the values for the paramters to test the proc? I'm told there's no way this could be done in Toad. Much appreciated!!!
I have to believe there's a way to poplate these variables but just not sure how to go about doing so...
DECLARE
myKey NUMBER;
arrayOfIntsFirst PL/SQL TABLE;
arrayOfIntsSecond PL/SQL TABLE;
arrayOfIntsThird PL/SQL TABLE;
BEGIN
myKey := NULL;
-- arrayOfIntsFirst := NULL; Modify the code to initialize this parameter
-- arrayOfIntsSecond := NULL; Modify the code to initialize this parameter
-- arrayOfIntsThird := NULL; Modify the code to initialize this parameter
MY_SCHEMA.PKG_MYPACKAGE.PERSISTCHANGES ( myKey, arrayOfIntsFirst,
arrayOfIntsSecond, arrayOfIntsThird );
COMMIT;
END;
Try something like:
declare
l_nums t_num_tab;
begin
l_nums := t_num_tab();
l_nums.extend(2);
l_nums(1) := 56;
l_nums(2) := 42;
for i in l_nums.first .. l_nums.last
loop
dbms_output.put_line( 'Number is: ' || l_nums(i) );
end loop;
end;
And Toad is just a development environment, it won't limit you from writing an anonymous block like above. The t_num_tab is defined as a table of numbers:
CREATE OR REPLACE TYPE t_num_tab as table of number;
You don't even need to formally extend the collection first and assign values. You can initialize in one step:
declare
l_nums t_num_tab;
begin
l_nums := t_num_tab(23,89,152);
...
end;
Read more here. Hope that helps.

oracle stored procedure OUT parameter vs. postgresql pl/pgsql function OUT parameter

I'm trying to port oracle stored procedures (plsql) to a postgresql function (pl/pgsql).
In oracle I can define a stored procedure with IN and OUT parameters.
CREATE OR REPLACE PROCEDURE MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
IS
BEGIN
bar := 1
END
This will store a value of 1 in the variable that is passed to the stored procedure.
I can call it as follows:
DECLARE
outValue NUMBER ;
BEGIN
Exec MY_TEST(10, outValue);
DBMS_OUTPUT.PUT_LINE('Value Returned Is : '||outValue) ;
END ;
In Postgresql (pl/pgsql) I can define a function like this one:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
BEGIN
bar := 1
END;
$body$
LANGUAGE PLPGSQL;
However I can not use the out parameter the same way as I could in oracle.
In postgresql the OUT parameter defines the return value.
In oracle stored procedures don't have return values, but instead write the output into the variable that is passed in the call
Is there something I overlooked, that would permit me to use pl/pgsql functions in a similar way as the stored procedure is used in the example above?
Any hints are greatly appreciated.
In PostgreSQL, PL/pgSQL or SQL functions take parameter values and return values.
They do not take pointers or references - so that the value of the referenced address could be manipulated.
You could do something like that in theory with a C-language function, where you can pass values by reference. However, de facto, you cannot. The manual warns:
Warning
Never modify the contents of a pass-by-reference input value. If you
do so you are likely to corrupt on-disk data, since the pointer you
are given might point directly into a disk buffer. The sole exception to this rule is explained in Section 35.10.
In short: what you are trying to do is impossible in PostgreSQL.
You don't need an OUT parameter for Postgres:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN integer)
returns integer
as
$body$
BEGIN
return 1;
END;
$body$
LANGUAGE PLPGSQL;
Then use it like this:
DO $body$
DECLARE
result integer;
begin
result := my_test(42);
raise notice 'Return value is: %', result;
end;
$body$
Take a read of this.
http://www.postgresonline.com/journal/archives/129-Use-of-OUT-and-INOUT-Parameters.html
In summary given the following stored function and the anonymous block to test this is one approach that the article will show.
CREATE OR REPLACE FUNCTION fn_plpgsqltestout(param_subject text,
OUT subject_scramble text, OUT subject_char text)
AS
$$
BEGIN
subject_scramble := substring($1, 1,CAST(random()*length($1) As integer));
subject_char := substring($1, 1,1);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
DO $body$
DECLARE
o_subject_scramble TEXT;
o_subject_char TEXT;
begin
--Option1
SELECT (fn_plpgsqltestout('This is a test subject')).* INTO o_subject_scramble,o_subject_char;
--Option2
SELECT (fn_plpgsqltestout('This is a test subject')).subject_scramble INTO o_subject_scramble;
SELECT (fn_plpgsqltestout('This is a test subject')).subject_char INTO o_subject_char;
--Option3
o_subject_scramble := (fn_plpgsqltestout('This is a test subject')).subject_scramble;
o_subject_char := (fn_plpgsqltestout('This is a test subject')).subject_char;
raise notice 'Return value is: %', o_subject_scramble;
raise notice 'Return value is: %', o_subject_char;
end;
$body$

Resources