Calling External routines from PL/SQL
(Part 1)

 

This document describes how to to call external routines (eg. written in C) from PL/SQL.

This page describes the creation of a function which takes two double precision floating point numbers and returns a double precision floating point number. This was used as an initial example as the parameter passing mechanism is simplest in this case.

This is the first page of three. The next page describes an example which accepts and returns variable length strings (VARCHAR2s) as parameters, and demonstrates the use of indicator parameters to hold the null/not-null status of the strings and their input and return lengths. The third page describes how to implement an external routine which can execute an arbitrary operating system command.

These notes apply specifically to Oracle running on Sun Solaris, although the principles probably apply to other platforms as well.

 

 

Pre-requisites

The ability to support external routines was introduced in Oracle 8. It is implemented using the Oracle Listener (ie. the database makes a call via the listener to the external routine, which is then invoked independently of the database. This protects the database from crashing if the external routine terminates abnormally.)

Consequently, entries need to be added to the listener.ora and tnsnames.ora files in the $ORACLE_HOME/network/admin directory on the database server. This only needs to be done once, not for each external routine.

 

More about parameters

Once the basic principles have been grasped, a lot of the complexity in calling external routines is in passing parameters between Oracle and the external routine.

There are two issues: mapping Oracle datatypes onto C datatypes, and handling parameter attributes (eg. how do we indicate that a returned value from an external routine is null, or tell the external routine the maximum length of a varchar2 parameter?)

 

Parameter passing by value or by reference

By default, most IN and RETURN parameters are passed by reference, as opposed to by value. (There are some exceptions, but they're not common. They're documented in the Oracle8i Application Developer's Guide - Fundamentals, Release 2 (8.1.6) manual.) This means that pointers to the locations of the values are passed to the external routine (as opposed to copies of the input parameter values), so the external routine's prototype must declare its input and return parameters as char*, int*, double* as appropriate.

 

Parameter datatypes

Although not complete, this table shows how PL/SQL variable data-types match to C datatypes. Note that the C datatype is dependent on whether the PL/SQL variable is an IN, OUT, IN OUT or RETURN parameter.

PL/SQL datatype parameter mode C datatype
DOUBLE IN, RETURN double
DOUBLE IN OUT, OUT double*
VARCHAR2 IN, RETURN char*
VARCHAR2 IN OUT, OUT char*
NUMBER IN, RETURN OCINumber*
NUMBER IN OUT, OUT OCINumber*

Note that the PL/SQL NUMBER datatype maps to the OCINumber* C datatype. This is not a native C datatype but is defined in the header file $ORACLE_HOME/rdbms/demo/oci.h (I think). Unfortunately as this is not a primitive C datatype, you cannot use the usual C arithmetic operators to manipulate it. Instead, you must use special OCI operators.
eg.

   OCINumberAdd() - add two OCINumbers,
   OCINumberMul() - multiply two OCINumbers  etc.
   

However, I've not yet been able to get external routines which pass NUMBER datatypes as parameters to work. As a get-around, create the external routine so that it passes DOUBLEs as its parameters, and move any NUMBER values to temporary DOUBLE variables to use as parameters in your PL/SQL program.

The same problem occurs with DATE variables (which map to OCIDate datatypes in external routines).

 

Parameter attributes

As well as the actual parameter values, we may wish to pass other information to the external routine about the parameters. For example, we may need to tell the external routine what the length of an input varchar2 parameter is (the length of the variable will be known, but what about the actual length of the string held in it?). We may also need to indicate to an external routine that a parameter is null: If we just pass the parameter values, then there is no way to tell the difference between the values 0 and null in a numeric field.

The next page works through an example which makes use of parameter attributes.

 

Related documentation

I didn't find any of the documentation particularly easy to follow as a step-by-step guide to calling an external routine, which is why I wrote this page.

However, I referred to the following documents whilst doing so.

 

Dave Wotton. 26/07/01. Last update: 30/10/01

Please email me if you've found this document useful or interesting or if you've got any questions about it. I appreciate the feedback. Feel free to recommend this page to others. Dave.

  Up one level