FOR EACH ROW
DECLARE
l_file_name images.file_name%TYPE;
l_directory VARCHAR2(30);
BEGIN
/* Determine the directory name */
DBMS_LOB.FILEGETNAME (file_loc => :NEW.image_file,
dir_alias => l_directory,
filename => l_file_name);
/* This will fail with DUP_VAL_ON_INDEX if the images
table
|| already contains a record with the new image_id.
*/
INSERT INTO images
VALUES (:NEW.image_id, l_file_name, :NEW.file_type,
:NEW.bytes);
IF :NEW.keywords IS NOT NULL THEN
DECLARE
/* Note: apparent bug prevents use of :NEW.
keywords.LAST.
|| The workaround is to store :NEW.keywords as a
local
|| variable (in this case keywords_holder.)
*/
keywords_holder Keyword_tab_t := :NEW.keywords;
BEGIN
FOR the_keyword IN 1..keywords_holder.LAST
LOOP
INSERT INTO keywords
VALUES (:NEW.image_id, keywords_holder
(the_keyword));
END LOOP;
END;
END IF;
END;
And finally, we can demonstrate how an insert would be made using the object view:
INSERT INTO images_v VALUES
(Image_t (1002, BFILENAME('WEBPIX','abc.gif'), 'GIF',
1024,
Keyword_tab_t('ALPHABET', 'LETTERS')));
Appendix C, Built-In Packages, contains information about these built-in packages.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous: 20.7 Object
Views Housekeeping
Oracle PL/SQL
Programming, 2nd Edition
Next: 21. External
Procedures
20.7 Object Views
Housekeeping
Book Index
21. External Procedures
The Oracle Library
Navigation
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous: 20.8 Postscript:
Using the BFILE Datatype
Chapter 21
Next: 21.2 Steps in
Creating an External
Procedure
21. External Procedures
Contents:
Introduction to External Procedures
Steps in Creating an External Procedure
Syntax for External Procedures
Mapping Parameters
OCI Service Routines
External Procedure Housekeeping
Examples
I've lost count of how many times I've heard the question "Can I call whatever from within Oracle?"
Typically, whatever is a program that interacts with the external environment: sending email, polling
or controlling hardware, or invoking the C library functions that PL/SQL lacks. Until very recently,
the standard answer was "No, you can't do that directly, but you can use a database pipe and write a
daemon that responds to requests on the pipe." An early paper on this approach[1] describes pipes as
an alternative to an even more primitive approach: using a temporary table as a sort of "bulletin
board" for interprocess communication.[
2]
[1] See Dan Bikle, "Inter-Application Communication Using Oracle7 Database Pipes,"
Select Vol. 1, No. 2, Winter 93/94, p. 34.
[2] The original paper did not present a true UNIX daemon (which has a specific
definition to UNIX and C programmers), but rather discussed the generic idea of a
continuously running process.
Temporary tables have serious limitations for this use, and even database pipe-based daemons have
their shortcomings. Packing and unpacking the contents of the pipe is a challenge; the daemon
typically execute in a separate Oracle session (and thus can't participate in a transaction), and the
solution is inherently single-threaded. Moreover, with the pipe solution, it's difficult to get return
values back from the daemon to the caller. What we need from Oracle is a fast, reliable way of
calling out to operating system commands or functions from within PL/SQL. Better yet, Oracle
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
should allow external routines to serve as user-defined functions so that you can use them in SQL
statements.
Enter external procedures. This long-awaited Oracle feature allows you to call anything that you can
compile into the native "shared library" format of the operating system. Yes, the external procedures
features is reliable; yes, it's multi-threaded; yes, communication is bidirectional; and yes, you can use
external procedures as user-defined functions in SQL.
Under UNIX, a shared library is a shared object or .so file; under Windows NT, it's a DLL
(dynamically linked library). You can write the external routine in any language you wish, as long as
your compiler and linker will generate the appropriate shared library format that is callable from C. In
Oracle 8.0, however, C will be the most common language for external procedures, since all of
Oracle's support libraries are written in C. Curiously, Oracle has named this feature external
"procedures," although the external routines you invoke are, technically speaking, C functions. (There
is no such thing as a procedure in C.) If the C function returns a value, you map it to a PL/SQL
function; if it returns no value, you map it to a PL/SQL procedure.
This chapter presents a few examples of external procedures (and functions). In addition, we review
the preconditions you must establish before they will work, and present the syntax for creating and
using this new feature. So, the next time you hear the question about calling whatever, you can
answer, "You bet!...in Oracle8."
This chapter does not discuss "distributed external procedures," which are a way of accessing non-
Oracle data sources from an Oracle server. Despite the name, these procedures are not closely related
to external procedures. Neither do we discuss at length the programming techniques that allow your
3GL code to call back to Oracle (we'll leave that to the books on programming C language access to
Oracle). But we do include some samples that you can use right away.
21.1 Introduction to External Procedures
How do external procedures work? How can I build my own? What are their advantages and
disadvantages? Before answering in detail, let's take a look at a quick example.
21.1.1 Example: Determining Free Disk Space on Windows NT
Here is an external procedure that will discover the amount of free space on a given disk drive. This
example is just to get you going. We won't try to explain all the details at this point. This example
was designed for Windows NT 4.0, but the idea can be applied to any operating system that meets the
requirements for external procedures. In this case, we simply make a call to the appropriate function
in the Windows kernel, rather than writing our own DLL.
Windows NT's kernel, kernel32.dll, contains a routine called GetDiskFreeSpaceA, which accepts a
drive letter as an input parameter and returns four statistics about the drive. When we register the
routine with PL/SQL, we will provide mappings for each of these parameters to a PL/SQL parameter.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Then, when we invoke the external procedure from a PL/SQL program, we'll use these statistics to
compute the free disk space.
First, we need to define a "library" to tell Oracle where the DLL lives:
/* Filename on companion disk: nt_space.sql */
CREATE OR REPLACE LIBRARY nt_kernel
AS
'c:\winnt\system32\kernel32.dll';
We'll create a package called disk_util that will contain our function, which we will call
get_disk_free_space as shown here:
CREATE OR REPLACE PACKAGE disk_util
AS
FUNCTION get_disk_free_space
(root_path IN VARCHAR2,
sectors_per_cluster OUT PLS_INTEGER,
bytes_per_sector OUT PLS_INTEGER,
number_of_free_clusters OUT PLS_INTEGER,
total_number_of_clusters OUT PLS_INTEGER)
RETURN PLS_INTEGER;
PRAGMA RESTRICT_REFERENCES (get_disk_free_space,
WNPS, RNPS, WNDS, RNDS);
END disk_util;
All the magic is in the package body, which uses the EXTERNAL clause rather than a BEGIN..END
block. This clause is where we define the interface between PL/SQL and the external routine:
CREATE OR REPLACE PACKAGE BODY disk_util
AS
FUNCTION get_disk_free_space
(root_path IN VARCHAR2,
sectors_per_cluster OUT PLS_INTEGER,
bytes_per_sector OUT PLS_INTEGER,
number_of_free_clusters OUT pls_integer,
total_number_of_clusters OUT PLS_INTEGER)
RETURN PLS_INTEGER
IS EXTERNAL
LIBRARY nt_kernel -- our library (defined
previously)
NAME "GetDiskFreeSpaceA" -- name of function in
kernel32.dll
LANGUAGE C -- external routine is
written in C
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
CALLING STANDARD PASCAL -- uses Pascal parameter
convention
PARAMETERS -- map PL/SQL to C
parameters by
-- position
(root_path STRING,
sectors_per_cluster BY REFERENCE LONG,
bytes_per_sector BY REFERENCE LONG,
number_of_free_clusters BY REFERENCE LONG,
total_number_of_clusters BY REFERENCE LONG,
RETURN LONG); -- "return code" indicating
success or failure
END disk_util;
Assuming that the DBA has set up the environment to support external procedures (see
Section
21.2.1, "Step 1: Set Up the Listener" later in this chapter), we can make an easy call to compute free
disk space on the C: drive:
SET SERVEROUTPUT ON SIZE 100000
DECLARE
lroot_path VARCHAR2(3) := 'C:\'; -- look at C drive
lsectors_per_cluster PLS_INTEGER;
lbytes_per_sector PLS_INTEGER;
lnumber_of_free_clusters PLS_INTEGER;
ltotal_number_of_clusters PLS_INTEGER;
return_code PLS_INTEGER;
free_meg REAL;
BEGIN
/* Call the external procedure. We ignore the return
code
|| in this simple example.
*/
return_code := disk_util.get_disk_free_space
(lroot_path,
lsectors_per_cluster, lbytes_per_sector,
lnumber_of_free_clusters,
ltotal_number_of_clusters);
/* Using the drive statistics that are returned from
the
|| external procedure, compute the amount of free disk
space.
|| Remember Megabytes = (Bytes / 1024 / 1024)
*/
free_meg := lsectors_per_cluster * lbytes_per_sector *
lnumber_of_free_clusters / 1024 / 1024;
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
DBMS_OUTPUT.PUT_LINE('free disk space, megabytes = '
|| free_meg);
END;
On my machine, this fragment produces the following output:
free disk space, megabytes = 214.53515625
Of course, you could put this computation in a named function or procedure, and even make it part of
the disk_util package.
Figure 21.1: Invoking an external procedure
21.1.2 Architecture
Let's take a look at what happens when you invoke an external procedure. As shown in Figure 21.1,
the process flow starts with a PL/SQL application that calls a special PL/SQL "module body." In our
example above, this body defines the get_disk_free_space function. PL/SQL then looks for a special
Net8 listener[
3] process, which should already be running in the background. At this point, the
listener will spawn an executable program called extproc. This process loads the dynamic library and
then invokes the desired routine in the shared library, whereupon it returns its results back to PL/
SQL.
[3] Net8 is the name for what was formerly the Oracle SQL*Net product. A Net8
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
listener is a background process, typically configured by the DBA, which enables other
processes to connect to a given service such as the Oracle server.
To limit overhead, only one extproc process needs to run for a given Oracle session; this process
starts with the first external procedure call and terminates when the session exits. For each distinct
external procedure you call, this extproc process loads the associated shared library, but only if it
hasn't already been loaded.
In case you are unfamiliar with dynamic linking, we've provided a few words of explanation. Calling
a dynamically linked routine simply maps the shared code pages into the address space of the "user"
process. Then, when that process touches one of the pages of the shared library, it will be paged into
physical memory, if it isn't already there. The resident pages of the mapped shared library file will be
shared automatically between users of that library.[
4] In practical terms, this means that heavy
concurrent use of your external procedure often requires a lot less computer processing power and
memory than, say, the primitive approach you might take with database pipes.
[4] This description applies to Sun's Solaris operating system as well as to Windows
NT. For a more detailed discussion from a UNIX perspective, see Adrian Cockroft's
performance column, "Which is Better -- Static or Dynamic Linking?" in SunWorld
Online, February 1996, at
/>perf.html.
21.1.3 Advantages
Oracle has provided a number of features that make external procedures truly an industrial-strength
resource. The four major features are summarized here.
●
Oracle external procedures use shared libraries rather than executables. Requiring the external
routine to be in a dynamically linked library, rather than in a statically linked module, helps
prevent heavy use of the procedure from consuming your machine. By contrast, static linking
means that all of the necessary libraries are actually copied into your compiled program, and
each execution requires its own address space. By requiring a library rather than a program,
Oracle further allows you to bundle many different external routines conveniently together
into a single shared library file. Co-locating a family of related routines in one shared library
provides the benefits of increased performance as well as manageability.
●
Oracle external procedures run in a separate memory space from the main kernel processes.
This is a good thing; it makes it easy to prevent your custom code from stepping on the
memory used by the database server. While it is technically possible to write an external
procedure that would crash the Oracle server, you have to set out to do so. If the external
procedure crashes, the companion process, extproc, returns an error to the PL/SQL engine,
which in turn reports it to the application.
●
External procedures provide full transaction support; that is, they can participate fully in the
current transaction. By accepting "context" information from PL/SQL, the procedure can call
back to the database to fetch data, make SQL or PL/SQL calls, and raise exceptions. In the
current release, utilizing these features requires some low-level Oracle Call Interface (OCI)
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
programming...but at least it's possible!
●
Oracle enforces the execution of external procedures with database-level security. At the first
level of security, only the DBA can execute or grant privileges on the required CREATE
LIBRARY statement that tells Oracle where the operating system file exists. At the next level
of security, users may only invoke the external procedure if they are granted EXECUTE
privilege on it.
21.1.4 Limitations
External procedures are not perfect, although their benefits far outweigh their shortcomings. I have
no doubt that external procedures will find uses in many applications. Here are some of the current
limitations of these procedures:
●
Despite the wonders of shared libraries, Oracle's architecture requires an unavoidable amount
of interprocess communication. Moreover, in Oracle 8.0, extproc is single-threaded; while
there is only one external procedure process per session, each session does require its own
process. (A future version may be multi-threaded, allowing sessions to share a smaller number
of extproc processes.)
●
External procedures (at least, as they are implemented in their first release) cannot pass
parameters of any user-defined type. Arguments must be conventional scalars. If you wish to
exchange objects or collections, a possible workaround is to write a PL/SQL module that
would split them up into scalars before calling the external procedure, and reassemble them
upon return.
●
With Oracle's current implementation, extproc closes the shared library after it's called,
meaning that libraries are not cached. Although this approach could save memory, it could
also mean more of a CPU hit for subsequent calls. In addition, the overhead for the first
external procedure call from a given session may result in a noticeable delay in response time;
however, subsequent calls are much faster.
Previous: 20.8 Postscript:
Using the BFILE Datatype
Oracle PL/SQL
Programming, 2nd Edition
Next: 21.2 Steps in
Creating an External
Procedure
20.8 Postscript: Using the
BFILE Datatype
Book Index
21.2 Steps in Creating an
External Procedure
The Oracle Library
Navigation
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous: 21.1 Introduction
to External Procedures
Chapter 21
External Procedures
Next: 21.3 Syntax for
External Procedures
21.2 Steps in Creating an External Procedure
Before you try external procedures, be sure that the machine where you're running the Oracle server
supports shared or dynamically linked libraries. Virtually all UNIX machines qualify, as do Windows
NT machines. If your own machine doesn't qualify, you can stop here, or you can investigate the use
of distributed external procedures.
These are your next tasks:
1. Ensure that your DBA has enabled a listener for external procedures in your Net8
environment. (This involves changes to tnsnames.ora and listener.ora.) This is a one-time job
for a given server.
2. Identify or create the .so or .DLL file which contains the shared library.
3. In Oracle, issue the SQL statement CREATE LIBRARY..., which defines an alias in the data
dictionary for the external shared library file. This registers the program with the database
engine so that it knows where to find it when it is called.
4. Create the PL/SQL function or procedure body, typically within a package, that will register
with PL/SQL the desired routine from the external library. The body of the procedure or
function will use the EXTERNAL clause in place of a BEGIN...END block.
And that's it! Let's look at each step in more detail, focusing on the implementation of a random
number generator for PL/SQL.
21.2.1 Step 1: Set Up the Listener
What actually happens when your code needs to use the external procedure? First, your code calls a
predefined PL/SQL body. When the PL/SQL runtime engine notices such a call, it looks for the
special Net8 listener named EXTERNAL_PROCEDURE_LISTENER, which in turn spawns a
session-specific process called extproc. It is extproc that invokes your routine in the shared library.
You need to create a new listener with a specific name, EXTERNAL_PROCEDURE _LISTENER.
This listener process will execute alongside other listener(s) that you already have running.
NOTE: You cannot change the names EXTERNAL_PROCEDURE_LISTENER or
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
extproc. In the listener.ora fragment given, everything that's not in lowercase italics
must match the listing.
In your listener.ora file, you will need the following entries:
/* filename on companion disk: lsnrfrag.ora /*
EXTERNAL_PROCEDURE_LISTENER =
(ADDRESS_LIST =
(ADDRESS =
(PROTOCOL=IPC)
(KEY=epsid)
)
)
SID_LIST_EXTERNAL_PROCEDURE_LISTENER =
(SID_LIST =
(SID_DESC=
(SID_NAME=epsid)
(ORACLE_HOME=full_directory_path)
(PROGRAM=extproc)
)
)
Where:
epsid
A short identifier that is used by Net8 to locate the listener. Its actual name is rather arbitrary,
since your programs will never see it. epsid has to be the same identifier in the address list and
in the SID list.
full_directory_path
The full pathname to your ORACLE_HOME directory, such as /u01/app/oracle/product/8.0.3
on UNIX or C:\ORANT on Windows NT. Notice that there are no quotes around the directory
name.
NOTE: If desired, listener.ora can point the listener's log into your desired directory:
LOG_DIRECTORY_EXTERNAL_PROCEDURE_LISTENER=/u01/
app/
oracle/admin/SID/logbook
And, for debugging, you can control "tracing" for your new listener with entries like
this:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
TRACE_DIRECTORY_EXTERNAL_PROCEDURE_LISTENER=/u01/
app/
oracle/admin/SID/logbook
TRACE_LEVEL_EXTERNAL_PROCEDURE_LISTENER=user
The tnsnames.ora file on the machine where the server is running will need an entry like the
following:
EXTPROC_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS =
(PROTOCOL=IPC)
(KEY=epsid)
)
(CONNECT_DATA=
(SID=epsid))
)
)
Again, epsid must match the key used in the listener.ora file.
TIP: If you intend to use external procedures on your server, remember to modify your
database startup and shutdown procedures to incorporate the listener start and stop
commands. On UNIX, this typically means editing a startup shell script.
On Windows NT, a Net8 listener is automatically installed as a system service the first
time you start it from the command line. However, the listener will not launch
automatically on boot unless you configure it to do so. You can use the control panel to
designate which listeners launch on system startup.
To remove the external procedure service on NT, delete the entries from listener.ora
and tnsnames.ora, and use the NT command instsrv <service name>
remove as follows:
instsrv
OracleTNSListener80external_procedure_listener
remove
(The instsrv utility is available in the NT 4.0 Server Resource Kit. Without it, you'll
probably have to edit the registry.)
After making these configuration file changes, you'll need to start the new listener process. In UNIX,
this is typically performed from the command line:
lsnrctl start external_procedure_listener
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
If your database server is running on Windows NT, the first time you start the listener, you'll use the
LSNRCTL80 command. From the command prompt, for example, the command would be:
LSNRCTL80 start external_procedure_listener
Thereafter (on NT) you can start and stop specific listeners from the Services component of the
Control Panel.
21.2.2 Step 2: Identify or Create the Shared Library
Step 1 is completely independent of any external procedure that you may create on your system. The
remaining steps, while specific to our random number procedure, include discussion that applies to
almost any external procedure.
Although later examples will show how to create your own shared libraries, let's start with an
example that requires no C language programming: calling a standard C library function, rand, which
generates a 16-bit random number. On many platforms, rand already exists in a shared object library;
on UNIX, it's often in /lib/libc.so, and on NT, in c:\winnt\system32\CRTDLL.DLL.[
5]
[5] Documentation on available library functions is commonly available in UNIX man
pages or in SDK documentation on other platforms.
A bit of background is in order for folks who haven't played with random number generators.
Random number algorithms have certain quirks you need to realize. First, such generators can be
deterministic; that is, the algorithm may return the same sequence of "random numbers" every time
unless first "seeded" with a quasi-random number. Often, the previous random number is stored and
used as a seed. But for the very first call, your program provides the seed, perhaps using some
function of the current system time. If you call rand later with an identical seed value, you will get an
identical pseudo-random sequence.
rand has a companion "seeding" function, srand, that allows you to supply your own seed value.
Calling srand before calling rand stores the seed value as a global in memory, for use by the next call
to rand. Subsequent calls from the same session need not call srand, since rand will re-seed itself.
21.2.3 Step 3: Issue CREATE LIBRARY Statement
Now that we have identified /lib/libc.so as the file containing our needed functions, the next thing we
have to do is create a library in the Oracle database. This is the easy part!
Creating a library is a way of telling Oracle that we want to refer to a specific shared object file by a
programmer-defined name. For many UNIX systems, /lib/libc.so will contain the needed function as
shown here:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
CREATE OR REPLACE LIBRARY libc_l
AS
'/lib/libc.so';
Executing this command requires the CREATE LIBRARY privilege (see
Section 21.3, "Syntax for
External Procedures" for more details).
Note that we have to use a fully qualified pathname; attempting to use an environment variable such
as $ORACLE_BASE in the filename will not work.
If your database server runs on Windows NT, your library would likely be created as follows:
CREATE OR REPLACE LIBRARY libc_l
AS
'c:\winnt\system32\CRTDLL.DLL';
Regardless of platform, you only need to create a single library in this fashion for each shared object
file you use. That is, even though you have only issued a single CREATE LIBRARY command for
libc.so (or CRTDLL.DLL), you can define any number of external procedures that use routines from
that file.
21.2.4 Step 4: Create the PL/SQL Body
The final step is to create a function or procedure definition which registers the desired routine from
the shared library. This feature lets you write the body of a PL/SQL procedure or function in C
instead of PL/SQL. To the caller it looks like any other PL/SQL subprogram.
Assuming that your C language skills are ready for any custom programming needed in Step 3, Step
4 is potentially the most complex one. Because of the differences between PL/SQL arguments and C
language arguments (in datatype, character set, whether they can be null, etc.), Oracle provides a lot
of "instrumentation" to allow you to properly map PL/SQL arguments to C language arguments. The
details of this instrumentation are described in
Section 21.4, "Mapping Parameters" later in this
chapter.
TIP: All of the samples in this chapter put the needed modules in a PL/SQL package.
One of many benefits of packages is that they enable us to use the
RESTRICT_REFERENCES pragma with our module(s). This pragma allows us to tell
Oracle that the user-defined function is "safe" and can therefore be used in SQL
statements. (See
Chapter 17, Calling PL/SQL Functions in SQL, for a full discussion of
this pragma.)
Returning once again to our random number example, the specification for an appropriate PL/SQL
package might look like this:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
/* Filename on companion disk: rand_utl.sql */
CREATE OR REPLACE PACKAGE random_utl
AS
FUNCTION rand RETURN PLS_INTEGER;
PRAGMA RESTRICT_REFERENCES (rand, WNDS, RNDS, WNPS,
RNPS);
PROCEDURE srand (seed IN PLS_INTEGER);
PRAGMA RESTRICT_REFERENCES (srand, WNDS, RNDS, WNPS,
RNPS);
END random_utl;
Notice that the package specification is completely devoid of clues that we intend to implement the
two subprograms as external procedures. We can't yet tell that rand and srand are any different from
conventional PL/SQL modules. And that is exactly the point! From a usage perspective, external
procedures are interchangeable with conventional procedures.
Our package body is blissfully short. By the way, assuming that your library is defined correctly, this
package will work as-is on either UNIX or Windows NT. (Even in cases where you can't use the
same external code on different operating systems, it may be possible to make the PL/SQL
specification the same. You could then make the external code the only thing that differs -- which
would be very desirable if you have to support multiple platforms.)
CREATE OR REPLACE PACKAGE BODY random_utl
AS
/* Tested with: (1) Solaris 2.5.1 and Oracle 8.0.3
|| (2) Windows NT 4.0 and Oracle 8.0.3
*/
FUNCTION rand RETURN PLS_INTEGER
/* Return a random number between 1 and (2**16 - 1),
using
|| the current seed value.
*/
IS
EXTERNAL -- tell PL/SQL that this is an
external procedure
LIBRARY libc_l -- specify the library that we
created above
NAME "rand" -- function's real name is lowercase
LANGUAGE C; -- we are calling a function written
in C
PROCEDURE srand (seed IN PLS_INTEGER)
/* Store a seed value used by external rand() function
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
*/
IS
EXTERNAL
LIBRARY libc_l
NAME "srand" -- srand (lowercase) is function's
real name
LANGUAGE C
PARAMETERS (seed ub4); -- map to unsigned four-
byte integer
END random_utl;
In this example, we have chosen to make the names of the PL/SQL modules identical to those in the
shared object library. It is not necessary that they match; in fact, you may wish to make them
different so that you can talk about (or document) the parameters independently.
Notice the PARAMETERS clause in the body of srand. Each of the formal parameters to the PL/SQL
module must have at least one corresponding entry in a PARAMETERS clause. Although there is an
extensive set of defaults which can eliminate the need for this clause, this explicit PARAMETERS
clause makes it perfectly clear how the parameters will be mapped between PL/SQL and C.
21.2.5 Using the rand External Procedure
Now that we've "registered" the external procedure with a PL/SQL package, we can test it:
SET SERVEROUTPUT ON SIZE 100000
DECLARE
rnd_value PLS_INTEGER;
seed PLS_INTEGER;
BEGIN
/* Generate a seed value from the current system time.
*/
SELECT TO_CHAR(SYSDATE, 'SSSSS') INTO seed FROM DUAL;
/* Call the srand external procedure to store our seed
in memory. */
random_utl.srand (seed);
/* Now demonstrate some random numbers. */
FOR v_cnt IN 1 .. 10 LOOP
rnd_value := random_utl.rand;
DBMS_OUTPUT.PUT_LINE ('rand() call #' || v_cnt ||
' returns ' || rnd_value);
END LOOP;
END;
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
This brief test routine simply seeds the library routine with a quasi-random number derived from the
current system time, then calls the random number generator ten times in a row.
One of our trial runs produced the following results:
rand() call #1 returns 27610
rand() call #2 returns 27964
rand() call #3 returns 27908
rand() call #4 returns 21610
rand() call #5 returns 14085
rand() call #6 returns 14281
rand() call #7 returns 9569
rand() call #8 returns 9397
rand() call #9 returns 24266
rand() call #10 returns 142
Limitations of rand
It doesn't take a rocket scientist to see that the numbers from our trial run are not totally random. This
isn't Oracle's fault! The problem lies with the algorithm used in rand. Moreover, rand returns
numbers only in the range 1 through 65535. Many C libraries include the XPG4 standard function
rand48, which overcomes these limitations at the expense of mildly increased complexity. See the
companion diskette for the source code of an external procedure that uses rand48 (the rand48ut.c and
rand48ut.sql files).
Previous: 21.1 Introduction
to External Procedures
Oracle PL/SQL
Programming, 2nd Edition
Next: 21.3 Syntax for
External Procedures
21.1 Introduction to External
Procedures
Book Index
21.3 Syntax for External
Procedures
The Oracle Library
Navigation
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous: 21.2 Steps in
Creating an External
Procedure
Chapter 21
External Procedures
Next: 21.4 Mapping
Parameters
21.3 Syntax for External Procedures
Now that we've gone through the basic steps and seen examples of the kind of code you must write in
order to take advantage of external procedures, let's explore each syntactic element in more detail.
21.3.1 CREATE LIBRARY: Creating the External Procedure Library
Step 3 in the random number generator example we presented in the previous section uses the SQL
statement CREATE LIBRARY. The general syntax for this command is:
CREATE [ OR REPLACE ] LIBRARY <library name>
AS
'<path to file>';
Where:
library name
A legal PL/SQL identifier. This name will be used in subsequent bodies of external
procedures that need to call the shared object (or DLL) file.
path to file
The fully qualified pathname to the shared object (or DLL) file. As shown above, it must be
enclosed in single quotes.
Here are some things to keep in mind when issuing a CREATE LIBRARY statement:
●
The statement must be executed by the DBA or by a user who has been granted CREATE
LIBRARY or CREATE ANY LIBRARY privileges.
●
As with most other database objects, libraries are owned by a specific Oracle user (schema).
Other users can refer to the library using owner.library syntax, or they can create synonyms
for the library (or use a synonym) if desired.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
●
Oracle doesn't check whether the named shared library file exists when you execute the
CREATE LIBRARY statement. Nor will it check when you later create an external procedure
declaration for a function in that library (see Step 4). If you have a syntax error in the path,
you won't know it until the first time you try to execute the function.
●
Setting up a listener is typically a DBA task performed (in UNIX, anyway) when the DBA is
logged on as the "oracle user." However, for security reasons, Oracle recommends setting up a
different OS-level user with a limited profile to run the listener for external procedures.
21.3.2 EXTERNAL: Creating the PL/SQL Body
In lieu of a BEGIN..END block, the body of your PL/SQL function or procedure must contain an
EXTERNAL clause. This section describes the content of this clause. Syntactically, it looks like this:
EXTERNAL LIBRARY <library name>
[ NAME <external routine name> ]
[ LANGUAGE <language name> ]
[ CALLING STANDARD C | PASCAL ]
[ PARAMETERS (<external parameter list>) ]
[ WITH CONTEXT ];
Where:
library name
Name defined previously in the CREATE LIBRARY statement.
external routine name
Name of the function as defined in the C language library. If the name is lowercase, you must
put double quotes around it. You can omit this parameter; if you do, the name of the external
routine must match your PL/SQL module's name (defaults to uppercase).
language name
Lets PL/SQL know the language in which the external routine is written. In early releases of
Oracle8, this parameter can only be C, which is the default.
CALLING STANDARD
On Windows NT, your application may use the Pascal calling standard, which means that
arguments are "reversed" on the stack and that your called function will deal with them
accordingly. CALLING STANDARD defaults to C if omitted.
PARAMETERS
This section gives the position and datatypes of parameters exchanged between PL/SQL and
C.
external parameter list
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
External parameters appear in a comma-delimited list. Each item in the list is one of three
things:
❍
The keyword CONTEXT, which is a placeholder for the context pointer
❍
Information about a formal parameter's mapping between PL/SQL and C
❍
The keyword RETURN and information about its mapping
The syntax and rules are discussed in "Mapping Parameters" below.
WITH CONTEXT
The presence of this clause indicates that you want PL/SQL to pass a "context pointer" to the
called program. The called program must be expecting the pointer as a formal parameter of
type OCIExtProcContext * (defined in the C header file ociextp.h).
This "context" that we are passing via a pointer is a data structure that contains a variety of
Oracle-specific information. The called procedure doesn't actually refer directly to the data
structure's content; instead, the structure simply facilitates other Oracle Call Interface (OCI)
calls which perform various Oracle-specific tasks. These tasks include raising predefined or
user-defined exceptions, allocating session-only memory (which gets released as soon as
control returns to PL/SQL), and obtaining information about the Oracle user's environment.
21.3.3 DROP: Dropping Libraries
The syntax for dropping a library is simply:
DROP LIBRARY <library name>;
The Oracle user who executes this command must have the DROP LIBRARY or DROP ANY
LIBRARY privilege.
Oracle does not check dependency information before dropping the library. This is useful if you need
to change the name or location of the shared object file to which the library points. You can just drop
it and rebuild it, and any dependent routines will continue to function. (More useful, perhaps, would
be a requirement that you use a DROP LIBRARY FORCE command, but such an option does not
exist).
Before you drop the library permanently, you'll probably want to look in the DBA_DEPENDENCIES
view to see if any PL/SQL module relies on the library.
Previous: 21.2 Steps in
Creating an External
Procedure
Oracle PL/SQL
Programming, 2nd Edition
Next: 21.4 Mapping
Parameters
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.