You are on page 1of 3

Call by Reference Parameters in PL/SQL (The NOCOPY Hint)

Amar Kumar Padhi, amar_padhi@hotmail.com

Prior to Oracle 8i releases, the IN OUT parameters in PL/SQL routines were


passed using the copy-in and copy-out semantics (call by value).

For example:

create or replace procedure cpy_chk


(pi_val in varchar2,
po_dat out date,
pio_status in out varchar2) is
begin
po_dat := sysdate;
pio_status := pio_status || 'amar is testing';
end;
When the above code is executed, Oracle will copy the value being passed to
parameters PO_DAT and PIO_STATUS in a separate buffer for the routine. On
completion of the routine, Oracle will copy the value being held in the PO_DAT
and PIO_STATUS parameters back to the original variable. This results in
multiple buffers being opened in memory and the overhead of copying data back
and forth. This can be huge in terms of CPU and Memory overhead if the
parameter is meant for large strings or collection objects.

The IN parameter is passed by reference; that is, a pointer to the actual IN


parameter is passed to the corresponding formal parameter. So, both parameters
refer to the same memory location and no copying overhead is involved.
However, the OUT and IN OUT parameters are passed by value.

To get around this, package variables were being used to pass values around.
Though serviceable as an alternative to prevent multiple buffers and copy
overhead, it resulted in higher maintenance cost.

From Oracle 8i onwards, the NOCOPY parameter hint has been introduced for
OUT and IN OUT parameters. Using this hint tells Oracle to make a call by
reference. Use this hint when there is no need to preserve the original value (in
case the called routine raises an exception). Oracle's internal benchmark testing
shows improvements of 30% to 200% for PL/SQL tables being passed as
parameters. NOCOPY is the ideal hint for OUT and IN OUT parameters when
the original value is not to be preserved (as is generally the case).

Here's an example:

create or replace procedure cpy_chk


(pi_val in varchar2,
po_dat out nocopy date,
pio_status in out nocopy varchar2) is
begin
po_dat := sysdate;
pio_status := pio_status || 'amar is testing';
end;

Drawbacks

NOCOPY is a hint and Oracle does not guarantee a parameter will be passed by
reference when explicitly mentioned. Here are some places where this is not
possible:

1. When the call is a remote procedure call


2. When the actual parameter being passed is an expression
3. When there is an implicit conversion involved

There may be other situations where Oracle may decide a call by value over a
call by reference. Since this is not clearly specified, it is advisable not to build any
process logic on this feature when exceptions being raised in the called routine
are being trapped in the calling routine.

Passing Large Data Structures with PL/SQL NOCOPY

The PL/SQL runtime engine has two different methods for passing parameter values
between stored procedures and functions, by value and by reference.

When a parameter is passed by value the PL/SQL runtime engine copies the actual value
of the parameter into the formal parameter. Any changes made to the parameter inside the
procedure has no effect on the values of the variables that were passed to the procedure
from outside.

When a parameter is passed by reference the runtime engine sets up the procedure call so
that both the actual and the formal parameters point (reference) the same memory
location that holds the value of the parameter.

By default OUT and IN OUT parameters are passed by value and IN parameters are
passed by reference. When an OUT or IN OUT parameter is modified inside the
procedure the procedure actually only modifies a copy of the parameter value. Only when
the procedure has finished without exception is the result value copied back to the formal
parameter.

Now, if you pass a large collection as an OUT or an IN OUT parameter then it will be
passed by value, in other words the entire collection will be copied to the formal
parameter when entering the procedure and back again when exiting the procedure. If the
collection is large this can lead to unnecessary CPU and memory consumption.

The NOCOPY hint alleviates this problem because you can use it to instruct the runtime
engine to try to pass OUT or IN OUT parameters by reference instead of by value. For
example:

procedure get_customer_orders(
p_customer_id in number,
p_orders out nocopy orders_coll
);
theorders orders_coll;
get_customer_orders(124, theorders);

In the absence of the NOCOPY hint the entire orders collection would have been copied
into the theorders variable upon exit from the procedure. Instead the collection is now
passed by reference.

Keep in mind, however, that there is a downside to using NOCOPY. When you pass
parameters to a procedure by reference then any modifications you perform on the
parameters inside the procedure is done on the same memory location as the actual
parameter, so the modifications are visible. In other words, there is no way to “undo” or
“rollback” these modifications, even when an exception is raised midway. So if an
exception is raised inside the procedure the value of the parameter is “undefined” and
cannot be trusted.

Consider our get_customer_orders example. If the p_orders parameter was half-filled


with orders when an exception was raised, then upon exit our theorders variable will also
be half-filled because it points to the same memory location as the p_orders parameter.
This downside is most problematic for IN OUT parameters because if an exception
occurs midway then not only is the output garbage, but you’ve also made the input
garbage.

To sum up, a NOCOPY hint can offer a small performance boost, but you must be careful
and know how it affects program behavior, in particular exception handling.