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

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$

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 call a stored function whose name is returned by another stored function in Oracle plsql?

I tried to make some version management for the salary computing in our payroll application.
I made a stored function for each formula version according to the legislation that applies at any given time, and then I stored the names of these functions in a table, together with the starting validity date of each function.
Then I made a function that retrieves the name (and returns this name as varchar) of the function applicable for a formula at a given time.
In the view that makes all the computing, I tried to call this function to retrieve the name of the function and use this returned name to call the formula function. All this is made in a select instruction.
I'm trying to call a function whose name is returned by the other function, like:
(function1(param1, param2, ...))(paramx, paramy, ...)
but this doesn't work.
Is there any way to use the name returned by function1 to call the function with that name and with input (parameters paramx, paramy, ...) ?
Functions are not first-class objects which can be returned from other functions, and the name of the function contained in a variable is not the actual code which can be executed. However, you can probably do what you're trying to do by using dynamic SQL:
DECLARE
param1 NUMBER;
param2 VARCHAR2(2000);
function_name VARCHAR2(2000);
paramx NUMBER;
paramy VARCHAR2(2000);
plsql_block VARCHAR2(2000);
result NUMBER; -- assumes the function returns a NUMBER
BEGIN
param1 := 123; -- or whatever is appropriate
param2 := 'abc'; -- or whatever is appropriate
function_name := function1(param1, param2);
-- assume that function_name now contains 'some_function'
paramx := 456; -- or whatever is appropriate
paramy := 'def'; -- or whatever is appropriate
plsql_block:= 'BEGIN :r := ' || function_name || '(:px, :py); END;';
-- plsql_block should now contain 'BEGIN :r := some_function(:px, :py); END;'
EXECUTE IMMEDIATE plsql_block USING IN OUT result, paramx, paramy;
DBMS_OUTPUT.PUT_LINE('result = ' || result);
END;
Best of luck.
If all the functions you might have to call are known and exist when the master function is compiled, then you can use the variable you have to decide which to call. As a very rough outline:
create function master_salary(p_date date)
return number as
l_function_name all_objects.object_name%type;
begin
l_function_name := choose_function(p_date);
case l_function
when 'function_a' then
return function_a;
when 'function_b' then
return function_b(some_arg);
when 'function_c' then
return function_c(some_arg, another_arg);
else
raise_application_error(-20001, 'Unknown function ' || l_function_name);
end case;
end;
/
This avoids dynamic SQL and lets you have functions with different numbers and/or types of arguments, and it's easier to follow what is happening.
If you're adding more functions on the fly then you probably shouldn't be - at least not outside some kind of source control and release mechanism, which would allow you to maintain the master function in step. You could, as a fall back, have the default case try to execute whatever function name you have dynamically (as Bob Jarvis shows) if it isn't one you're expecting; but you'd need consistent argument numbers and data types, and it potentially opens up a vulnerability if the table you get the function name from could be modified.

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

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.

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.

Resources