You are on page 1of 220

652 OracteS pUSeL programming

n the last chapter we examined the intersession


communication features available with OracleZ:
pipes and
alerts. These allow you to implement basic
messaging
capabilities. OracleB provides a more compreheniivJ
messaging system, however, known as Oracle
Advanced
Queuing. We will examine'this system in rhis .n"pt",; i"J"ii"g
detailed examples.

lntroduction
ln general, an apprication can be thought
of as a set of programs or
modules that communicate with each "other.
Tl" ..r"irnication consists
of messagel passed between the modures. rn
orderlolrr.n a system to
work reliably, the message system must have.uioin
plperties:
I Reliability given message shourd be guaranteed to
{ be derivered
aqired recipient or iecipients, anj the message shourd
lllf
delivered only once.
be

I scalabirity The system shourd not rose performance


as the number
of modules andlor messages increases.
t Restorabirity Ng messages shourd be rost as a
resurt of a system
failure. rn case of a fairure, a, messager th"i*ur"
sent and
committed should be recoverable.

ffi ffi tfl l.f ,t*1.":13:?::3,'Ji;ii;i..,ii,::ll:::xlTiT.?,,.,^o


g system. Such systems are often
as Tp monitors, but Oracle Advanced
Oracle8. Because it is implemented
vides the same benefits as other

t Queues are stored in database tables, so they are part of the


standard backup and recovery mechanis, ,upport"d
They can also be exported and imported. '-'--'--- -/ '
by Oracle.

I All queue operations take place using SeL, so they


have full
transactional control.
Chapter l7: Oracle Advanced Queuing 653

f Messages are object types, so they can have varying attributes


within them. One message can consist of several attributes, all
sent as a unit.

Because of this implementation, Oracle Advanced Queuing satisfies the


rel iabi lity, scalabi I ity, and restorabi I ity requi rements. The messagi ng
features available with OracleZ (pipes and alerts) are not nearly as full
featured, and thus Advanced Queuing is a better mechanism for
implementing new appl ications.

Components of Advanced Queuing


The basic component of any queuing system is a queue. ln general, a
queue is essentially an ordered list of items. The enqueue operation inserts
new items into the queue, and the dequeue operation removes items from
the queue. For Oracle Advanced Queuing, the items in a queue are
messages, which in turn consist of both the data and routing information.
Queues are not separate data dictionary objects; they are stored in queue
tables. The following sections describe the components of Oracle
Advanced Queuing.

ENQUEUE Operation
ENQUEUE insefts a message into a given queue. Along with the messaBe
data itself (which can be either an object type or RAW data) is the routing
information, which specifies how the message is to be inserted and who
the intended recipients are. The routing information can optionally include
the following:

I Correlation identifier This is a user-specified identifier to specify


individual messages. The correlation identifier can be used to
dequeue this message later.
I Subscription and recipient lists A queue can have a recipient list
that specifies which readers will get an enqueued message. A
message can specify a subscription list, which can override the
queue's recipient list.
I Priority Messages can have a priority. The higher priority messages
will be inserted into the queue ahead of lower priority messages.
654 Oracte8 pUSeL programming

I Crouping Messages can be grouped. A message group


will be
retrieved by a.given process a1 a unit, and
this [rJ."r, ."n
successivelydequeue from the.queue, eventuaily
getting all the
messages in the group. Other dequeue operations"by
other
processes wirr receive the foilowing messages,
and not the
messages contained in the group.

I Time specification A message can be set to


expire if it is not
dequeued by a certain time. i-f it expires, it is
moved to an exception
queue. A minimum time can arso be specified,
before which the
message will not be dequeued.

I Transaction protection The ENeUEUE


of
the current transaction, or it can be a sep
is
immediately committed on its own, thus
immediately available for a subsequent DEeUEUE.

DEQUEUE Operation
DEQUEUE retrievei a from a specified queue. rf there are no
lnessage
messages/ then DEeUEUE wi1 brock untir
, r"ri"g" i, ,"iriuu"o, or untir
the operation rimes out. DEeUEUE has the foilowifrg
opaioni,

I Dequeue mode you can specify that the message


remains in the
queue after you retrieve it (BRowsE mode),
or tfiat it be dereted
from the queue after you retrieve it.
I Time-out lf there
1l".no wartrng messages, the DEeUEUE
operation can specify how long io wait.
I Transaction protection The DEeUEUE operation
can be part of
the current transaction, or it can be a separate
transaction that is
immediately committed on its own.
I Message specification DEeUEUE can retrieve
the first waiting
message, or the next message after a given
message, or it can serect
based on a correlation ideniifier.

I Retries with delays rf the DEeuEUE fairs and


the transaction is
rolled back, the message can be made avairabre
foireprocessing
after a user-specified deray, for a user-specified
number of retries.
Chapter l7: Oracle Advanced Queuing 655

Queues
There are two types of queues: user queues and exception queues. A user
queue contains normal messages. lf a message expires (is not dequeued
before the expiration date), it is moved to an exception queue, which
contains messages that had an error.

Queue Tables
Queues are stored in a queue table. A queue table by default contains one
exception queue, although it can contain more if desired. Queue tables are
created first, then the queues within them are enabled for ENQUEUE and
DEQUEUE operations.

Agents
An agent is a user of a queue. A producing agent (or producer) is someone
who inserts messages into a queue, and a consuming agent(or consumer) is
someone who dequeues messages. The same agent can act as both a
producer and a consumer.

Time Manager
The time manager is an Oracle background process that is used to manage
message expiration, along with the delay and retry operations. lf this
process is not started, then these features are not available.

NOTE
ln order to start the time manager, set the
AQ_TM_PROCESSES i n it.ora parameter to
1. The default value is 0, indicating that no
time manager processes should be started. A
value of 1 indicates that one process should
be started. As of Oracle] release 8.0.3, there
can be at most one time manager process.

lmplementation of Advanced Queuing


Oracle Advanced Queuing is implemented using two PL/SQL packages,
DBMS_AQ and DBMS_AQADM. DBMS_AQ contains the ENQUEUE and,
556 Oracle8 PUSQL Programming

DEQUEUE operations, while DBMS_AQADM contair.rs administrative


functions. Table 17-1 lists the sr"rbprograms in these packages, and they are
described in detail in the remainder of this chapter. For more information
on DBMS_AQ and DtsMS_AQADM, see the Orar /eB Server Application
Developer's Cuide.

Queue Operations
The DBMS_AQ package implements some supporting types as ',vell as the
ENQUEUE and DEQUEUE operations. The adnrinistration operations for
queues (creating and droppinB queues and queue tables, for example) are
implemented in the DBMS_AQADM package.

Supporting Types
DBMS_AQ defines several supporting types, which are used for subsequent
calls. The types are listed in Table 17-2, and described in the following
sections. These types consist of index-by tables, and PL/SQL records.

SYS.AQ$_AGENT
This type identifies an agent, which can serve as a producer or consumer of
a queue. lt is implemented as a PL/SQL record, and defined as

TYPE SYS.AQ$-ACENT IS RECORD (

name VARCHAR2(30),
address VARCHAR2(30),
protocol NUMBER);

where the name field contains the name of the agent. As of Oracle8
Release 8.0, the address and protocol fields are unused and are reserved
for future use.

AQ$_RECTPTENT_L|ST_T
This type is a list of agents that can receive a message. lt is paft of the
message properties, and thus is used for both the ENQUEUE and
Chapter l7: Oracle Advanced Queuing 657

Package Subprogram Description


DBMS_AQ ENQUEUE Enqueue operation, to insert
messages into a queue, with
the specified routing
information.
DBMS-AQ DEQUEUE Dequeue operation, to
retrieve messages from a
queue, with the specified
selection i nformation.
DBMS-AQADM CREATE-QUEUE-TABLE Creates a queue table with
the specified parameters.
DBMS-AQADM CREATE-QUEUE Creates a queue within an
existing queue table.
DBMS_AQADM DROP-QUEUE Drops a given queue from
its queue table.
DBMS-AQADM DROP-QUEUE-TABLE Drops a queue table, along
with its component queues.
DBMS_AQADM ALTER-QUEUE Changes options for a
given queue.
DBMS-AQADM ADD SUBSCRIBER Adds a subscriber to a
given queue.
DBMS-AQADM REMOVE SUBSCRIBER Drops a subscriber from
a given queue.
DBMS-AQADM CRANT TYPE ACCESS Crants permissions to the
queue administrator.
DBMS-AQADM QUEUE_SUBSCRIBERS Returns the subscribers for
a grven queue.
DBMS-AQADM START TIME MANACER Enables the time manager
background process.
DBMS-AQADM STOP TIME MANACER Disables the time manager
background process.

TABLE l7- 1. DBMS_AQ and DBMS_AQADM Packages


658 OracleS PUSQL programming

Type Name Description Used in


SYS.AQ$_ACENT A producer or Contained as part of
consumer of a Ae$_nfCtplENT_LIST_T,
message and thus ENeUEUE and
DEQUEUE
AQ$-nrctplENT-Llsr-T Lisr of agents to contained as part of
receive a message MESSACE_pROpERTtES T
and thus ENQUEUE and
DEQUEUE
a
MESSACE_PROPERTIES_T properries of ENeUEUE and
message DEeUEUE
ENQUEUE_OPTIONS_T Oprionsforenqueue ENeUEUE
DEQUEUE_OPTIONS_T Optionsfordequeue DEeUEUE

TABLE l7-2. DBMS_Ae Supporting Types

DEQUEUE operations. The recipient list is impremented as an index-by


table of agents, and is defined as follows:

TYPE AQ$-RECIPIENT_LIST_T IS TABLE OF SYS.AQ$_AGENT


INDEX BY BINARY INTECER;

MESSAGE_PROPERTIES T
The MESSAGE-PROPERTIES-T type is used in both the ENeUEUE and
DEQUEUE operations, and describes the options available for a given
message. lt is a PUSQL record, defined as follows:

TYPE MESSACE_PROPERTI ES_T IS RECORD(


priority BtNARy_tNTECER DEFAULT 1,
delay Bl NARY_I NTECER DEFAU LT NO_DELAY,
expi ration Bl NARY_| NTECER DEFAU LT N EVER,
correl ation VARCHAR2(1 2S) DEFAU LT N U LL,
attempts Bl NARY_I NTECER,
rec i pi ent_l i st AQ$_ReCt pt ENT_Lt ST_T,
Chapter l7: Oracle Advanced Queuing 659

exception-queue VARCHAR2(5 1 ) DEFAULT NULL,


enqueue-time DATE,
srare B I NARy_l ITITECER);

The fields of this type are described in Table 1 7-3.

Field Datatype Description


priority BINARY INTECER Priority of the accompanying
message. A smaller number
indicates a higher priority.
Priorities can be any integer
value, including negative values.

delay BINARY INTECER Delay for the accompanying


message. delay can be either
NO_DELAY, indicati ng that
the message is available for
immediate dequeuing, or the
number of seconds to wait.
A dequeue that specifies the
message identifier wi ll
override the delay. A message
that is delayed is enqueued in
the WAITINC state and then
moves to the READY state
after delay seconds.
expiration BINARY INTECER Time in seconds after which
the message will expire if it
is not dequeued. lf
expiration is set to NEVER,
the message will not expire.
The expiration time is
counted after the delay time,
if it is specified. After the
expiration, the message is
moved to the exception
queue in the EXPIRED state.

TABLE I7-3. MESSAGE-PROPERTIES_T


660 OracleS PUSQL Programming

Field Datatype Description


correlation VARCHAR2(128) Correl ation identifier.
Messages can be retrieved by
correlation identifier if desired.
attempts BINARY INTECER Number of attempts made to
dequeue this message. This
parameter is set at
DEQUEUE time only.
recipient_list AQ$-RECIPIENT_LIST_T List of recipients for this
message. This parameter is
read at ENQUEUE time and
is not returned by the
DEQUEUE operation.
exception_queue VARCHAR2(51) Exception queue for this
message. lf the message
expires or the number of
deq ueue attempts exceeds
max retries, then the
message is moved to the
exception queue in the
EXPIRED state. lf
exception_queue is not
specified, then the message is
moved to the default exception
queue for the queue table. lf
exception_queue is specified
but this queue is not valid at
the time of the move, the
message is moved to the
default exception queue for the
queue table and a message is
written to the alert log.

TABLE I7.3. MESSAGE-PRzPERTIES r(continued)


Chapter l7: Oracle Advanced Queuing 651

Field Datatype Description


enqueue_time DATE Time when the message was
enqueued. This parameter is
returned by DEQUEUE and
is set automatically by the
system at ENQLJEUE time.
state BINARY INTECER State of the message at the
tinre of the DEQUEUE. This
parameter cannot be set by
ENQUEUE, and is updated
automatically by the system
as appropriate. The values
are WAITINC (the message
delay has not yet been
reached), READY (the
message is ready to be
processed), PROCESSED (the
message has been processed
but was retained in the
queue), and EXPIRED (the
message has been moved to
the exception queue).

TABLE I7.3. MESSAGE PRaPERTIES r (continued)

ENQUEUE_OPTtONS_T
This type is used to specify the options for the ENQUEUE operation, other
than those of the message itself. lt is defined as a PL/SQL record:

TypE ENQUEUE_OPT|ONS_T tS RECORD (

ity Br NARy_t NTECER DEFAU LT ON_COMMtT,


vi si bi I

relative_msgid RAW(1 6) DEFAULT NULL,


seq uence_dev i ati o n B I NARY_l NTEC E R DE FAU LT N U LL);
652 Oracle8 PUSQL programming

The fields within the type are described in Table 1Z_4.

Field Datatype Description


visibility BINARY INTECER Specifies the transactional
behavior. lf visibility is
ON_COMMIT (the default),
then the enqueue wilt be
complete when the current
transaction commits. lf visibil ity
is IMMEDIATE, then the
enqueue constitutes a separate
transaction that is immediately
committed. Even if the current
transaction is rolled back, the
enqueue will be performed.
relative_msgid RAW lf BEFORE is specified in
seq ue nce_dev i ati on, then the
accompanying message will be
inserted before the message
identified by relative_msgid. tf
sequence_devi ation is not
specified, then rel ati ve_msgid
is ignored.
sequence_deviation BINARY INTEGER Specifies the location of the
accompanying message in the
queue. Valid values are
BEFORE (the message is to be
enqueued ahead ofthe message
specified by rel ative_msgid),
TOP (the message is to be
enqueued ahead of any other
messages), and NULL (the
priority of the message specifies
its position in the queue). The
default is NULL.

TABLE l7-4. ENeUEUE_opnoNS r


Chapter l7: Oracle Advanced Queuing 663

DEQUEUE_OPTIONS_T
This record is used to specify the options for the DEQUEUE operation, as
opposed to the options in the message itself. lt is defined with

TYPE DEQUEUE_OPTTONS_T lS RECORD (

consumer name VARCHAR2(3O) DEFAULT NULL,


dequeue_mode B I NARY-I NTEGER DEFAU L T REMOVE,
navigation BINARY-INTECER DEFAULT NEXT-MESSACE,
v i sibi I ity B I NARY_I NTEC ER DEFAU LT ON_COMMIT,

wair Bl NARY_I NTECER DEFAULT FOREVER,


msgid RAW(I6) DEFAULT NULL,
correl ati on VARCHAR2(30) DEFAU LT N U LL);

The fields of DEQUEUE-OPTIONS-T are described in Table 17-5.

Field Datatype Description


consumer VARCHAR2(3O) Name of the consumer to receive the
name message. lf specified, only those
messages which match this consumer are
eligible for the dequeue. lf the queue is
not set up for multiple consumers,
consumer name should be NULL.
dequeue_ BINARY_INTEGER Specifies the locking behavior of the
mode dequeue. Valid values are BROWSE
(read the message without locking or
dequeuing, similar to a SELECT),
LOCKED (read and obtain a write lock
on the message, similar to a SELECT FOR
UPDATE), and REMOVE (read and then
update or delete the message, depending
on the retention properties of the queue).
REMOVE is the default.

TABLE l7-5. DEeUEUE_2PT|?NS_T


664 OracteS pUSeL programming

Field Datatype Description


navigation BTNARY-TNTECER specifies the position of the
message to
be retrieved. After the position is
determined, the search criterion
(consisting of consumer_name, msgid
and correlation) is applied. Valid vilues
are NEXT_MESSACE (retrieve the next
available message matching the search
criterion), N EXT_TRANSACTTON (skip
the remainder of the current message
group, if any, and retrieve the first
available message in the next message
group), and FIRST_MESSACE (retriev*e the
first available message matching the search
criterion, searching from the start of the
queue). NEXT_MESSACE is the default.
visibility BINARY_INTECER Specifies whether the DEeUEUE is
part
of the current transaction. lf ON_COMMIT
(the default), the dequeue operation
is part
of the current transaction. lf IMMEDIAiE,
the dequeue is a transaction of its own,
that is committed immediately
watt BINARY-INTECER Number of seconds to wait if there
is
currently no message available which
matches the search criterion. This is
ignored if messages in the same group
are being dequeued. Valid valuei are
FOREVER (no timeout, this is the defauit),
NO WAIT (do not wait, return
immediately if no message is available),
or the number of secondi to wait.
msgid RAW(16) Specifies a message identifier for the
message to be dequeued.
correlation VARCHAR2(30) Specifies a correlation identifier for the
message to be dequeued.

TABLE l7-5. DEeuEUE_opTtoNS_T(continued)


Chapter l7: Oracle Advanced Queuing 565

Enumerated Constants
Many of the fields for the supporting types in DBMS-AQ are enumerated
constants. The fields are defined as BINARY-INTEGER, and DBMS-AQ
defines the appropriate values. When setting these options, they need to be
prefixed with the package name-DBMS-AQ.IMMEDIATE, for example.
The following table lists all of the enumerated constants, their values, and
where they are used.

Field Values Where Used


sequence- BEFORE, TOP ENQUEUE-OPTIONS-T
deviation
visibility IMMEDIATE, ENQUEUE-OPTIONS-T
ON COMMIT
dequeue-mode BROWSE, LOCKED, DEQUEUE-OPTIONS-T
REMOVE
navigation DEeUEUE-optoNS-r
illilME:li8E:
NEXT TRANSACTION
wait FOREVER, NO-WAIT DEQUEUE-OPTIONS-T
state WAITINC, READY, MESSACE-PROPERTIES-T
PROCESSED, EXPIRED
delay NO-DELAY MESSACE-PROPERTIES-T
expiration NEVER MESSACE PROPERTIES T

ENQUEUE
DBMS-AQ.ENQUEUE can accept a message payload that is either an
object type or RAW. lt is defined as follows:

PROCEDURE ENQUEUE (

queue-name I N VARCHAR2,
enqueue-options I N ENQU EU E-OPTIONS-T,
mess age-p ro perties I N M ESSAC E-P ROPE RTI ES-T,
payload lN message-tYPe,
msgid OUT RAW);

The parameters are described in the following table:


665 OracleS PUSQL programming

Parameter Datatype Description


queue_name VARCHAR2
Queue into which the
message should be inserted.
enqueue_ ENQUEUE_OPT|ONS T Options for the enqueue.
options The fields in this type are
described in Table 1Z-4.
message_ MESSACE pROpERTtES T Properties for the message.
properties The properties are returned on
a subsequent DEQUEUE
operation. The fields in this type
are described in Table 1Z-3.
payload message_type Message data. This can be
either a RAW or object type. lf
it is an object type, it must
match the type specified for
the queue when it was created.
msgid RAW Message lD returned. This lD
can be used to dequeue this
particular message,
regardless of priority or delay.

The sequence-deviation field in the enqueue options specifies a


.
relationship between two messages. lf this is speciiied, then there are
two
restrictions for the delay and priority of the enqueued message:

I The delay of the newly enqueued message must be ress than or


equal to the delay of the message before which this message is to
be enqueued.

I The priority of the newly enqueued message must be greater than


or
equal to the priority of the message before which this "message is to
be enqueued.

DEQUEUE
DBMS-AQ.DEQUEUE is also overloaded to accept either a RAW or object
type message. lt is defined with
Chapter l7: Oracle Advanced Queuing 667

PROCEDURE DEQUEUE (

queue_name I N VARCHAR2,
dequeue_options I N DEQU EU E_OPTIONS_T,
message_properTies OUT MESSACE PROPERTIES T,
payload OUT messa ge_type,
msgid OUT RAW);

The parameters are described in the following table.

Parameter Datatype Description


queue_name VARCHAR2 Name of the queue
to query for
messages.
dequeue_options DEQUEUE OPTIONS T Optionsforthe
dequeue. The fields in
this type are described
in Table 17-5.
message_properties MESSACE PROPERTIES T Properties of the
retrieved message.
The fields in this
type are described in
Table 17-3.
payload message_type Either the RAW or
object type message.
msgid RAW Message lD foi
this message.

The search criterion for the dequeue is determined by the consumer_


name, msgid, and/or correlation fields in dequeue_options. Only messages
in the READY state are dequeued unless msgid is specified.

Queue Administration
ln this section, we will examine the DBMS_AQADM packages, and the
privileges required to use queues. We will also examine the data dictionary
views relevant to queues.
568. OracleS PUSQL programming

DBMS_AQADM Subprograms
The DBMS-AQADM package defines subprograms to adminisrer
queues
and queue tables. Each of the subprograms islescribed in
this section.

CREATE_QUEUE_TABLE

t A default exception queue associated with this queue table, called


aq$_gueue_ table_e.
I A read-only view which can be used to query the queue, cailed
aq$_queue_table.
I An index for the time manager, called aq$_gueue_ tahle_t.
r An index (or index organized table) for multiple consumer queues,
cal led aq$_gueue_ tahle_i.

The syntax for CREATE_eUEUE_TABLE follows, and the parameters


.
described in the following table.
are

PROCEDU RE CREATE-QU EU E-TAB LE(


queue_table I N VARCHAR2,
queue_payl oad_type I N VARCHAR2,
storage_clause lN VARCHAR2 DEFAULT NULL,
sort_list lN VARCHAR2 DEFAULT NULL,
multiple_consumers tN BOOLEAN DEFAULT FALSE,
message_grouping tN BtNARy_tNTECER DEFAULT NONF
COMMCNT IN VARCHAR2 DEFAULT NULL,
auto_commir tN BOOLEAN DEFAULT TRUE);
Chapter l7: Oracle Advanced Queuing 669

Parameter Datatype Description


queue_table VARCHAR2 Name of the queue table to
be created.
queue_payload_type VARCHAR2 Type of the user data in this
queue. This should be either
RAW or an object type.
storage_clause VARCHAR2 Storage parameter to be used for
the CREATE TABLE statement.
This can include the
specification of clauses such as
TABLESPACE, PCTFREE,
PCTUSED, LOB, INITTRANS,
and MAXTRANS. See the SQt
Reference Guide for a full
description of the storage clause.
sort list VARCHAR2 Defines the columns to be used as
the sort key, and thus the sort
behavior of queues created in this
table. See the description following
this table for more details.
multiple_consumers BOOLEAN lf TRUE, queues created in this
table can have multiple
consumers per message. The
user must have been granted
type access using
CRANT TYPE ACCESS first. lf
FALSE (the default), queues
created in this table can have
only one consumer per message.
570 OracleS PUSQL programming

Parameter Datatype Description


message_grouping BTNARY_INTEGER lf NoNE (the default), then each
message in queues created in
this queue table is treated
individually. lf
TRANSACTIONAL, then al I

messages enqueued as part of


one transaction are considered
pan of the same message group,
and thus are dequeued together.
comment VARCHAR2 Comment for the newly created
table. This will be recorded in
the queue catalog.
auto commit BOOLEAN lf TRUE (the default), the current
transaction is committed before
the queue table is created, and
the operation is persistent when
CREATE_QU EU E_TABLE returns.
lf FALSE, then the create is part
of the current transaction, and
will become persistent when the
current transaction commits.

soRT-Llsr The sort-lrsr parameter of CREATE_eUEUE_TABLE


specifies the sorting properties of the queue table, anj hence the properties
of any queues created in this table. lt is a comma-separated list of sort
columns. The allowable sort columns are 'priority' and ,enq_time,. rf
both
are specified, the first column defines the most significant orour. lf
sort list
is not specified, then the default is to sort by enqueue time, which
implements a FIFO queue.
lndividual messages can still be dequeued regardless of the sort order by
specifying a correlation or message identifier at Jequeue time.

DROP_QUEUE_TABLE
This procedure drops the specified queue table. All the queues in the
table
must be stopped and then dropped before the table ."n b" dropped.
The syntax is
Chapter l7: Oracle Advanced Queuing 67 I

PROCEDU RE DROP-QU EU E_TABLE(


queue_table I N VARCHAR2,
force lN BOOLEAN DEFAULT FALSE,
auto commit lN BOOLEAN DEFAULT TRUE);

The parameters for DROP_QUEUE_TABLE are described in the


following table.

Parameter Datatype Description


queue_table VARCHAR2 Queue table to be dropped.
force BOOLEAN lf TRUE, all queues in the table are stopped
and dropped automatically. lf FALSE (the
default), an error is returned if there are still
queues in the table.
auto commit BOOLEAN lf TRUE (the default), the current transaction
is committed before the queue table is
dropped, and the operation is persistent
when the procedure returns. lf FALSE, the
operation will become persistent when the
cu rrent transaction commits.

CREATE_QUEUE
CREATE_QUEUE creates a queue in the specified queue table. The queue
name must be unique within the current schema, and both enqueue and
dequeue operations on the queue are disabled by default. After the queue is
created, it must be enabled using START_QUEUE. The syntax for
CREATE_QUEUE is

PROCEDU RE CREATE-QU EU E(
queue_name lN VARCHAR2,
queue_table I N VARCHAR2,
queue_type lN BINARY_INTECER DEFAULT NORMAL_QUEUE,
max retries lN NUMBER DEFAULT 0,
retry_delay lN NUMBER DEFAULT 0,
retention time lN NUMBER DEFAULT 0,
dependency_tracking I N BOOLEAN DEFAULT FALSE,
comment lN VARCHAR2 DEFAULT NULL,
auto cammit lN BOOLEAN DEFAULT TRUE);
672 Oracle8 PUSQL Programming

and the parameters are described in the following table.

Parameter Datatype Description


queue_name VARCHAR2 Name of the queue to
be created.
queue_table VARCHAR2 Queue table which will
contain the newly created
queue. The new queue will
assume the properties of the
queue table, such as sort order
and payload type.
queue_type BINARY INTEGER Type of queue. Valid values
are NORMAL_QUEU E (creates
a normal queue)or
EXCEPTION_QU EU E (creates
an exception queue). The
default is NoRMAL_eUEUE.
max retries NUMBER Maximum number of dequeue
attempts with REMOVE
specified. lf an agent dequeues
a message, but then rolls back,
the count is incremented.
When it reaches max_retries,
the message is moved to the
exception queue. The default
is0, indicating no retries.
retry_delay NUMBER Delay in seconds between
retries. lf max_retries is 0, then
retry_delay has no effect. lf the
queue has multiple
consumers, then retry_del ay
cannot be specified.
Chapter l7: Oracle Advanced Queuing ' 673

Parameter Datatype Description


retention time NUMBER Number of seconds for which
a message will be retained in
the queue table after being
dequeued. Valid values are
INFINITE (the message will be
retained forever) or the
number of seconds. The
default is 0.
dependency_tracking BOOLEAN This parameter is reserved tbr
future use. The default is
FALSE, and it is currently an
error to specify TRUE.
comment VARCHAR2 Description for the newly
created queue. This comment
will be added to the queue
catalog.
auto commit BOOLEAN lf TRUE (the default), the
current transaction is
committed before the queue is
created, and the operation is
persistent when
CREATE_QU EUE returns. lf
FALSE, the operation will
become persistent when the
current transaction commits.

DROP_QUEUE
DROP_QUEUE drops an existing queue from its containing queue table.
The qr.reue rnust h.tve been stopped with STOP_QUEUE prior to beine
dropped. All of the qLrelre data is deleted as part of the drop operatiorr.
DROP_QUEUE is defined with
674 Oracle8 PUSQL Programming

PROCEDURE DROP-QUEUE(
queue_name I N VARCHAR2,
auto commir lN BOOLEAN DEFAULT TRUE);

where queue_name is the name of the queue to be dropped, and


auto_commit specifies the transactional behavior of this operation. lf
auto_commit is TRUE (the default), the current transaction is committed
first. lf auto commit is FALSE, the queue will be dropped when the current
transaction commits.

ALTER_QUEUE
This procedure is used to modify certain properties of a queue. only the
max_retries, retry_delay, and retention_time properties can be modified. to
change other properties, the queue must be dropped and recreated. The
syntax for ALTER_QUEUE follows, and the parameters are described in the
following table.

PROCEDURE ALTER _QUEUE(


queue_name I N VARCHAR2,
max retries lN NUMBER DEFAULT NULL,
retry_delay lN NUMBER DEFAULT NULL,
retention time lN NUMBER DEFAULTNULL,
auto commit lN BOOLEAN DEFAULT TRUE);

Parameter Datatype Description


queue_name VARCHAR2 Name of the queue to be modified.
max retries NUMBER Maximum number of dequeue attempts
with REMOVE specified. lf an agent
dequeues a message, but then rolls back, the
count is incremented. When it reaches
max_retries, the message is moved to the
exception queue.
retry-delay NUMBER Delay in seconds between retries. lf
max_retries is 0, then retry_delay has no
effect. lf the queue has multiple consurners,
then retry_delay cannot be specified.
Chapter l7: Oracle Advanced Queuing 675

Parameter Datatype Description


retentton Ume NUMBER Number of seconds for which a message will
be retained in the queue table after being
dequeued. Valid values are INFINITE (the
message will be retained forever) or the
number of seconds. The default is 0.
auto commtt BOOLEAN lf TRUE, the current transaction is
committed before the queue is created,
and the operation is persistent when
ALTER_QUEUE returns. lf FALSE, the
operation will become persistent when the
current transaction commits.

START_QUEUE
START QUEUE is used to enable a queue for enqueue and/or dequeue
operations. A queue must be started after it is created. Only dequeues are
allowed on an exception queue, so enabling an exception queue for
enqueue has no effect. The queue is started when this procedure returns,
even if the transaction is rolled back. START QUEUE is defined with

PROCEDU RE START-QU EU E(
queue_name I N VARCHAR2,
enqueue lN BOOLEAN DEFAULT TRUE,
dequeue lN BOOLEAN DEFAULT TRUE);

where queue_name is the name of the queue to be started, and enqueue


and dequeue specify that the queue should be enabled for enqueue and
dequeue operations, respectively.

sToP_ QUEUE
STOP QUEUE is used to disable enqueue and/or dequeue operations for
the specified queue. A queue cannot be stopped if there are oLrtstanding
transactions against it. The queue is stopped when this procedure returns,
even if the current transaction is rolled back. STOP_QUEUE is defined with

PROCEDURE STOP-QUEUE(
queue,,nanle I N VARCHAR2,
67 5 OracleS PUSQL programming

enqueue lN BOOLEAN DEFAULT TRUE,


dequeue lN BOOLEAN DEFAULT TRUE,
wait lN BOOLEAN DEFAULT TRUE);

where queue-name is.the name of the queue to be stopped, and Enqueue


a.nd dequeue specify that the queue shourd be disablei for enqueue
and
dequeue operations, respectively. rf wait is TRUE, then this cali will block
until all outstanding transactions against queue_name are committed or
rolled back. lf waitis FALSE, then STop_euEUE returns immediately with
success or failure.

ADD_SUBSCRIBER
ADD-SUBSCRIBER will add a default subscriber ro a queue. This is only
valid on queues which can have multipre consumers. ADD suBSCRIBER
takes effect immediately, and the current transaction is committed. The
user
must have been granted access to this queue with CRANT TypE ACCESS.
The syntax is

PROCEDURE ADD SUBSCRIBER(


queue name lN VARCHAR2,
subsc ri ber I N SYS.Aeg_ACENT);

where queue-name is the queue to be modified, and subscriberisrhe


new subscriber.

REMOVE_SUBSCRIBER
This procedure will remove the specified subscriber from the specified
queue. lt takes effect immediately, and the current transaction is
committed. As paft of the remove, all references to the subscriber are
deleted from existing messages. The user must have been granted access to
this queue with CRANT_TYPE_ACCESS. The syntax is

PROCEDURE REMOVE-SU BSCRI BER(


queue_name I N VARCHAR2,
subscr i ber I N SYS.AQg_AC E NT);

where queue-name is the qLreue to be modified, and subscriberisrhe


subscriber to be removed"
Chapter l7: Oracle Advanced Queuing 677

QUEUE_SUBSCRIBERS
This function will return a PL/SQL table of the subscribers for a given
queue. The syntax is

FU NCTION QU EU E-SU BSCRI BERS(


queue-name I N VARCHAR2)
RETU RN AQ$-SU BSCRI B ER-LIST-T;

GRANT-TYPE_ACCESS
CRANT-TYPE-ACCESS is used to enable a given user to perform queue
administrative operations. Unless this is done, the user cannot execute
CREATE-QU EU E-TAB LE, CREATE-QU EU E, ADD-SU BSCRI B ER, ANd
REMOVE SUBSCRIBER. lt is defined with

PROCEDURE CRANT TYPE ACCESS(


user name lN VARCHAR2);

where user-name is the database user to be granted the permission.

START_TIME-MANAGER
This procedure enables the time manager process. The AQ-TM-PROCESS
init.ora parameter must have been set to 1 first, which starts the process at
database startup time. The time manager is started when this call returns,
even if the current transaction is rolled back. START TIME-MANACER
takes no arguments and is defined with

PROCEDURE START TIME MANACER;

STOP TIME MANAGER


STOP-TIME-MANACER disables the time manager. lt does not kill the
process, however. Rather, it causes the process to stop its operations. The
time manager is stopped when this call returns, even if the current
transaction is rolled back. STOP-TIME-MANACER takes no arguments and
is defined with

PROCEDURE STOP TIME MANACER;


678 OracleS PUSQL Programming

Queue Privileges
ln order to use the DBMS-Ae or DBMS_AeADM package, there are
certain steps which must be performed by the system administrator.
There are two roles which must be granted, foilowed by access to the
AQ object types.

AQ_ADMINISTRATOR ROLE
The role AQ-ADMINtsrRAToR RoLE is predefined by oracle. tt contains
all the necessary cRANTs to allow the grantee EXECUiE permission on
DBMS-AQ and DBMS_AQADM. This role shourd be granted to users who
will need to administer queues.

AQ_USER_ROLE
The AQ-USER-ROLE role, on the other hand, just granrs EXECUTE on
DBMS-AQ. This role should be granted to users who will need to enqueue
and dequeue messages, but not adminrster queues.

Access to AQ Obiect Types


Finally, the queue administrator needs access to the object types used in
queues. This is done with the DBMS AeADM.CRANT TypE ACCESS
procedure, which should be called by sys. This enables the grantee to run
thc CREATE-QU EU E-TAB LE, CREATE-QU E U E, ADD-S U BSLRI B ER, ANd
REMOVE_SU BSCRI BER routi nes.

Queues and the Data Diction ary


There are several data dictionary views which enable you to find out
information about the gueues in the system. These inciude a view for every
queue table, as well as views for allqueues and all queue tables.

Queue Table View


Every time you create a queue tab.le (with DBMS_AeADM.CREATE_
F
QUEUE-TABLE), a view is created in the current schema which coniains H
F
information about the messages in that table. Each row provides data about F

one message. The name of this view is aq$gueue_ table_name, and has the tr

structure defined in the following table:


h

$
4:
€n';
tti;.
{

4
4
q
1

t"
1 Chapter l7: Oracle Advanced Queuing 579
d
t
*
*1
Column Datatype Description
queue VARCHAR2(30) Name of the queue
$
l: containing this message
t msg_id RAW(16) Message identifier
4
L
corr id VARCHAR2(30) Correl ation identifier
t NUMBER Priority of this message
;1 msg_priority
msg_state VARCHAR2(9) State of this messagHne
'tl

Y,
of READY, DELAYED,
.i PROCESSED, or EXPIRED
.ii

I delay DATE Delay specified at


enqueue time
expiration NUMBER Expiration specified at
enqueue time
enq_time DATE Time this message was
enqueued
User lD of the user who
1l

enq-user-id NUMBER
issued the enqueue
enq-txn-id VARCHAR2(30) Transaction lD of the
enqueue
deq_time DATE Time this message was
dequeued
deq-user id NUMBER User lD of the usei who
issued the dequeue

deq-txn-id VARCHAR2(30) Transaction lD of the


dequeue
retry_count NUMBER Number of dequeue
attempts for this message
excepti on_queue-owner VARCHAR2(30) Owner of the exception
queue specified at enqueue
time, if any
exception-queue VARCHAR2(30) Exception queue sPecified at
enqueue time, if any
user data object_type Data that was enqueued
or RAW
680 OracleS PUSQL programming

DB S/USER_QUEUE_TABLES
The ntains information abouiall the queue tables
in th e_tables contains information about the
current user's queue tables. They both have the following
structure:

Column Datatype Description


owner VARCHAR2(30) Owner of the queue rable
(dba_queue_tables on y)I

queue_table VARCHAR2(3O) Name of the queue table


type VARCHAR2(Z) payload rype-OBJECT
or RAW
sort order VARCHAR2(22) Sort order specified ar queue
table creation time
TECIPIENTS VARCHAR2(B) Eithcr SINCLE Or MULTIPLE
message_€rouping VARCHAR2(13) Either NONE or
TRANSACTIONAL
user comment VARCHAR2(50) Comment specified at queue
table creation time

Column Datatype Description


owner VARCHAR2(3O) Owner of the queue
(dba_queues only)
name VARCHAR2(30) Queue name
queue_table VARCHAR2(30) eueue table in which this
queue resides
qid NUMBER Unique queue identifier, generated
when the queue was created
queue_type VARCHAR2(15) One of NORMAL_eUEUE or
EXCEPTION-QUEUE
Chapter l7: Oracle Advanced Queuing 68 I

Column Datatype Description


max retries NUMBER Number of dequeue attempts
allowed
retry_delay NUMBER Delay between retries
enqueue-enabled VARCHAR2(7) Either YES or NO
dequeue_enabled VARCHAR2(7) Either YES or NO
retention, VARCHAR2(40) Number of seconds a message is
retained after a dequeue
user comment VARCHAR2(50) Comment specified at queue
creation

Extended Examples
ln this section, we features of Advanced Queuing
will examine many of the
through a series of examples. The examples illustrate the following features:

I Creating queues and queue tables


I Simple enqueue and dequeue
I Clearing a queue
I Enqueue and dequeue bY PrioritY

I Enqueue and dequeue by correlation or message identifier

I Browsing a queue
I Using exce,lption queues
I Dropping queues

Creating Queues and Queue Tables


The first step is to grant the necessary permissions to the queue owner.
Suppose we are want to create queues under the example schema. ln order
to do this, we must Brant the AQ-ADMINISTRATOR ROLE role first, and
then grant type access to example. So, we could execute the followirrg
statements while connected as system or another DBA accor.rnt:
682 OracleS PUSQL Programming

GRANT AQ-ADMINISTRATOR-RoLE To examp1e,.


BEGIN DBMS_AQADM. GRANT-TYPE-ACCESS ( . example, ) ; END;

once this is done, we can proceed to create queue tables and queues. The
following script will create and start all of the queues used in these
examples.

-- Available online as createq.Eql


CREATE OR REPLACE TYPE MeSSageObJ AS OB.]ECT (
title VARCHAR2 (30),
daLal NUMBER.
data2 VARCHAR2 (100) ,
data3 DATE,

MEMBER PROCEDURE Print (v_Messaqe fN VARCHAR2)


);
CREATE OR REPLACE TYPE BODY MessageobJ AS
.MEMBER PROCEDURE Print(v_Message IN VARCHAR2) IS
BEG]N
DBMS_OUTPUT.PUT_LINE(v_Message I l, z' | | titfey ;
DBMS_OUTPUT.PUT(,Data 1: , ll aatarl;
DBMS\ouTp.uT.puT('Dara 2: , ll aatazl;
DBMS_OUTPUr.PUT_LrNE(,Dara 3: , ll dara3);
END Print;
END;

BEGIN
-- Create a simple table, with all of the defaults. This will a11ow
-- FIFO queues, with no message grouping. or multiple consllmers.
DBMS_AQADM . CREATE-QUEUE_TABLE (

queue_table =>'SimpleQTab',
queue3ayJ.o.ad_type =>'Messageobj,,
comment => 'Si-mp1e Queue fable,);
-- Create a simple queue contained within SimpleeTab. Again, use
-- the default parameters.
DBMS-AQADM. CREATE_QUEUE (
queue_name =>'SimpfeQ',
queue_table =>'Sj-mpleQTab',
comment => 'Simple Queue, ) ;

-- Enabl-e enqueue and dequeue operations for Simplee.


DBMS_AQADM. START-QUEUE ( ,
S impleQ , ) ;
Chapter l7: Oracle Advarrced Qtreuing 683

-- Create an exception queue within SimpleQTab.


DBMS-AQADM. CREATE_QUEUE (

queue_name =>'ExceptionQ',
queue_table =>'SimpleQTab',
queue_tltpe => DBMS_AQADM. EXCEPTION_QUEUE,
conment => 'Exception Queue') ;

-- Enabl-e dequeue operations for ExceptionQ.


, ExceptionQ ,
DBMS-AQADM. START-QUEUE ( , FALSE, TRUE ) ;
END;

BEGIN
-- Create a priority queue tab1e, by specifying the sort order.
-- This queue has no message grouping or multi-ple consumers.
DBMS-AQADM, CREATE-QUEUE _TABLE (

queue_table =>' Pri-orityQTab',


queue3ayload_t1pe =>'Messageobj',
sort_lj-st -> 'prior j-ty, enq_t j.me' ,
comment => 'Pri-ority Queue table');

-- Create a prj-ority queue contained within PriorityQTab. Again,


-- use the default parameters.
DBMS-AQADM. CREATE_QUEUE (

queue_name =>'PriorityQ',
qr:eue-table =>' PriorityQTab',
comment => 'Pri-ority Queue');

-- Enabl-e enqueue and dequeue operations for PriorityQ.


DBMS-AQADM. START_QUEUE ( , PTiOTi IYQ , ) ;
END;

Simple En{ueue and Dequeue


The following example illustrates a series of enqueue and dequeue
operations on SimpleQ.

-- Available online as simple.sql


DECLARE
v-Message MessagreObj ;
v_Enqueueop t i on s DBMS-AQ . ENQUEUE-O PT I ONS-T ;
v_DequeueOptions DBMS-AQ. DEQUEUE-OPTIONS-T;
v_Mes sageProper t i e s DBMS-AQ . MES SAGE-PROPERTI ES-T ;
v_MsgID RAW(16);
584 OracleS PUSQL Programming

c_NurMessages CONSTANT INTEGER := 10;

e_QTimeOut EXCEpTTON;
PRAGMA EXCEPTION_INIT(e-QTimeouI, _25228) ;
BEGIN
FOR v_Counter IN 1. . c_NumMessages LOOP
-- Create a messaqe to enqueue.
v_Message:=
MessageObj(,Message , ll v_Counter, v_Counter * 10,
'abcdefghijklmnopqrstuwrnqzz,, SYSDATE + v_Counter) ;

-- Enqueue j-t with the default options.


DBMS_AQ. ENQUEUE (

queue_name =>,Simpfee,,
enqueue_options => v_EnqueueOptj_ons,
messageJropert i es => .v_Mes sagepropert j_es,
payload => v_Messag'e,
msgid => v_MsgID);
END LOOP;

-- Commit all the enqueues.


COMMTT;

-- Loop until there are no more messages to dequeue.


BEGIN
LOOP
-- Dequeue the first message into v_Messag'e, waitj_ng a maximum
-- of 1 second.
v_Dequetreoptions.wait := 1;
DBMS_AQ. DEQUEUE (
queue_name =>,Simpfee',
dequeue_optj_ons => v_Dequeueoptions,
messageJroperties => v_Messageproperties,
payload => v_Message,
msgid => v_MsgID);

-- And print j-t.


v_Message.Print (,After dequeue, ) ;
END LOOP;
EXCEPTION
WHEN e_QTimeout THEN
-- End of the queue reached.
NULL;
END;
Chapter l7: Oracle Advanced eueuing 685

-- Commit a1l_ the dequeues.


COMMIT;
END;

When run in sQL*Plus, this script produces the following output:

After dequeue: Message 1


Data 1: 10 Data 2: abcdefghijklmnopqrstuvwxlzz Data 3: 15_AUG_97
After dequeue: Message 2
Data 1: 20 Data 2: abcdefghijklmnopqrstuvwxyz Data 3: 16-AUG-97
After dequeue: Message 3
Data 1: 30 Data 2: abcdefghijklrnnopqrstuvwxyz Data 3: 17-AUG-97
After dequeue: Message 4
Data 1: 40 Data 2: abcdefghijklmnopqrstuvwxlrz Data 3: 1B_AUG_97
After dequeue: Message 5
Data 1: 50 Data 2: abcdefghijklmnopqrstur,rwxyz Data 3: 19_AUG_9?
After dequeue: Message 6
Data 1: 60 Data 2: abcdefghijklmnopqrstuvr^rxyz Data 3:20-AUG-97
After dequeue: Message 7
Data 1: 70 Data 2: abcdefghijklmnopqrstuvwxyz Data 3:2L-AUG-9'/
After dequeue: Message 8
Data 1: 80 Data 2: abcdefghijklmnopqrstuvwxyz Data 3:22-AUG-9'7
After degueue: Message 9
Data 1: 90 Data 2: abcdefghijklmnopqrstuvwxyz Data 3z 23-AUG-97
After degueue: Message 10
Data 1: 100 Data 2: abcdefghijklmnopqrstuvwxyz Data 3:24-ArJG-9i
The messages have been dequeued in the same order in which they
were enqueued, because this is a FIFO queue.

Clearing a Queue
We can adapt the second portion of the above exampre into a procedure
that will delete all the messages in a queue, by simply dequeuing until
there are none left:

-- Available ontine ae clearq.sql


CREATE oR REPLACE pRocEDURE Cleareueue(p_eueueName rN VARCHAR2) AS
v_Message MessageObj;
v_DequeueOpti ons DBMS_AQ . DEeUEUE_OpTIONS_T ;
v_Mes s aqePropert i e s DBMS_Ae . MES SAGE_PROPERTI ES_T ;
v_MssID naW(16);
586 Oracle8 PUSQL Programming

e_QTimeOut EXCEpTION;
PRAGMA EXCEPTTON_rNIT (e_eTimeOut, _252281 ;
BEGIN
-- Loop until there are no more messages to d.equeue.
BEGIN
LOOP
-- Dequeue the first message into v_Messaqe, waiting a maximum
-- of 1 second.
v_DequeueOptions.waj_t := 1;
DBMS-AQ. DEQUEUE (

queue_name =>,Simplee,,
dequeue_options => v_Dequeueoptions,
mes sag'ejropert ies = > v_Mes saqepropert j_ es,
payload => v_Messag,e,
msgid => v_MsgID);
END LOOP;
EXCEPTION
WHEN e_eTimeout THEN
-- End of the queue reached.
NULL;
END;

-- Commj_t al_l- the dequeues.


COMMIT;
END;

Enqueue and Dequeue by Priority


The following example illustrates a series of enqueues and dequeues on a
priority queue.

-- Available online as priority.sqt


DECLARE
v_Message MessageObj;
v_EnqueueOp t i ons DBMS_AQ . ENeUEUE_OpT I ONS_T ;
v_Dequeueop t i ons DBMS_Ae . DEeUEUE_OpT I ONS_T ;
v_Me ssa ge Proper t i es DBMS_Ae . MES SAGE_PROpERT I E S_T ;
v_MsgID RAW(16);

c_NumMessages CONSTANT INTEGER := 10;

e_QTimeout EXCEpTION;
PRAGMA EXCEPTTON-IN]T(e-QTimeout, -25228) ;
BEGTN
FOR v_Counter IN 1 . . c_Nr.unMessages LOOP
Chapter l7: Oracle Advanced Queuing 587

-- Create a message to enqueue.


v_Messaqe :=
MessageObj('Messaqe' ll v-Counter, v-Counter * 10.
' zl.xwvutsrq)onmlkjihqfedcba', SYSDATE + v-Counter) ;

-- Enqueue it with priority equal to -v-Counter. Thus the last


-- message to be enqueued will have the highest priority'
v-MessageProperties.prj-ority := -v-Counter;
DBMS_AQ. ENQUEUE (

queue-name =>'PriorityQ',
enqueue-options => v-EnqueueOptions,
messagejroperties => v-MessageProperties,
payload => v-Messagle,
msgid => v-MsgID);
END LOOP;

-- Commit al-l the enqueues '


COMMIT;

-- Loop until there are no more messages to dequeue'


BEGIN
LOOP
--Dequeuethefirstmessageintov-Message,waitinqamaximum
-- of 1 second. : '
v-DequeueOPtions.waj-t =
: 1;
DBMS_AQ. DEQUEUE (

queue-name =>'PriorityQ'
dequeue options => v-Dequeueoptions,
messageiroperties => v-MessageProperties,
payload -> v-Message'
msgid => v-MsgID);

-- And Print it.


v-Messaqe.Print ( 'After dequeue' ) ;
END LOOP;
EXCEPTION
WHEN e-QTimeout THEN
-- End of the queue reached.
NULL;
END;

-- Commit al-f the dequeues.


COMMTT;
END;
688 OracleS PUSQL Programming

When we run this script in selxplus, we get the foilowing output:


After dequeue: Messaqe 10
.- Data 1: 100 Data 2: zyxwr,'utsrryonmlkjihgfedcba Data 3 : 24-AUG-97
After dequeue: Message 9
Data 1: 90 Data 2: zyxwwutsrponmlkjihgfedcba Data 3: 23-AUG-97
After dequeue: Message B
Data 1: 80 Data 2: zy>srluLsrryonmlkjihgfedcba Data 3; 22-AUG-g-l
After dequeue: Messag'e 7
Data 1: 70 Data 2: zyxtrtttttsrq>onmlkjihgfedcba Data 3; 2r-ArJG-9i
After degueue: Messag.e 6
Data 1: 60 Data 2: zyxwwutsrryonmlkjihgfedcba Data 3: 20-AUG-97
After dequeue: Message 5
Data 1: 50 Data 2: zyxmruLsrponmlkjihgfedcba Data 3: 19-AUG-97
After dequeue: Messaqe 4
Data 1: 40 Data 2: zyx,ttLsrryonmlkjihgfedcba Data 3: 1B-AUG-97
After dequeue: Messaqe 3
Data 1: 30 Data 2: zyxvnruLsrq>onmlkjihgfedcba Data 3: 17-AUG-9-1
After dequeue: Messag.e 2
Data 1: 20 Data 2: zyxtttruLsrryonmlkjihgfedcba Data 3: 16-AUG-97
After dequeue: Messag.e 1
Data 1: 10 Data 2: zy>c,ttsroLsrponmlkjihgfedcba Data 3: 15-AUG-97

Note the difference in output between the simple queue and priority
queue. The priority queue dequeues by priority, resulting in a reverse list
from the simple queue.

Enqueue and Dequeue by


Correlation or Message ldentifier
The correlation identifier allows you to specify a user-defined string to
identify a message, or series of messages. The correlation lD is pasied as
part of the message properties at enqueue time, and as part of the dequeue
options at dequeue time. The following block iilustrates using the
correlation identifier to dequeue a series of related messages.
Dequeuing by a message identifier, however, wiil match exactly one
message. The message lD is returned by the enqueue, and can be passed as
part of the dequeue options at dequeue time. This is illustrated by ihe
following block as well.
-- Available online as ident.sql
DECLARE
Chapter l7: Oracle Advanced Queuing 689

v-Message MessageObj i
v-Enqueueop t i ons DBMS-AQ . ENQUEUE-OPT I ONS-T ;
v_Deqr-reueOpt j- ons DBMS-AQ . DEQUEUE-OPT IONS-T ;
v_Me ss ag'eProper t i e s DBMS_AQ . MES SAGE-PRoPERT I ES-T ;
v_MsgID naw(16);
v_TigerMsglD RAW(16);

c-NlirnMessaqes CONSTANT INTEGER := 10;

TYPE t-Correlations IS TABLE OF VARCHAR2(30)


INDEX BY BINARY-INTEGER;
v-Correlations t-Correlat j-ons ;

e_QTj-meOut EXCEPTION;
PRAGMA EXCEPTION-INIT (e-QTimeout, -25228) ;
BEGIN
-- Initialize the array of correl-ation identifiers.
-- There will be a total- of 5 different correl-ation IDs, with two
-- messages getting each one.
FOR v_Counter IN 1..c-NurMessages LOOP
IF MoD(v-Counter, 5) = 1 THEN
v-Correl-ations (v-Counter) : = 'Lion' ;
ELSIF MOD(v-Counter, 5) = 2 THEN
v-Correlations (v-Counter) : = 'Tiger' ;
ELSIF MoD(v-Counter, 5) = 3 THEN
v-Correlations (v-Counter) i='Bear' ;
ELSIF MoD(v-Counter, 5) = 4 THEN
v-Correfations (v-Counter) : = 'Fish' ;
ELSE
v-Correlations (v-Counter) :='Horse' i
END IF;
END LOOP;

FOR v_Counter IN 1..c-NumMessaqes LOOP


-- Create a message to enqueue.
v_Messaqe :=
Messageobj('Message' ll v-counter, v-Counter * l-0,
'abcdefghijkJ-mnopqrstul D<yz', SYSDATE + v-Counter) ;

v-MesEageProperties . correlation : = v-Correlations (v-Counter) t


DBMS-OUTPUT.PUT-LINE('Enqueing nfessage' | | v-Counter | |

' with correlation ID ' | | v-correlations (v-Counter) ) ;


DBMS-AQ. ENQUEUE (

queue-name =>'SimPleQ',
enqueue-options => v-EnqueueOptions,
590 Oracle8 PUSQL Programming

messag,ejroperties => v_Messageproperties,


payload => v_Message,
msgid => v MsgID);

-- Save one of the tiger message ID's.


IF v_Correlatibns(v_Counter) = ,Tiger, THEN
v_TigerMsgID := v_MsgID;
END IF;
END LOOP;

-- Commit a1l- the enqueues.


COMMIT;

-- Dequeue only the messages with correlatj-on ID ,Fish,.


BEGTN
LOOP
-- Dequeue the first message into v_Message, wait.ing a maximum
-- of l- second.
v_DegueueOptions.wait := 1;
v_Dequeueogtions.correlalion :=,Fish, i
DBMS_AQ. DEQUEUE (

queue_name =>'Simp1ee,,
dequeue_options => v_DequeueOptions, i
messageJropertj_es => v_Messageproperties,
payload => v_Messag.e,
msgid => v_MsgID);

-- And print it.


v_Message.Print(,After dequeue with correl-ation ID Fish, );
END LOOP;
EXCEPTION
WHEN e_QTimeout THEN
-- End of the queue reached.
NULL;
END;

-- Dequeue only the message with the saved messag.e fD.


v_Dequeueoptions.correlation : = NULL;
v_DequeueoptJ-ons.nsgid : = v_TigerMBgIDt
DBMS-AQ. DEQUEUE (

queue_name => ' S j_mp1ee, ,


dequeue_options => v_DequeueOptions,
messageJroperties => v_Messageproperties,
payload => v_Message,
msgid => v_MsgrID) ;
Chapter l7: Oracle Advanced Queuing 691

-- And print it.


v_Message.Print('After dequeue with saved message ID');

-- Clear afl the remainingr messages.


ClearQueue ('SimpleQ' ) ;

-- Commit alt the dequeues


COMMIT;
END;

This scrrpt produces the followinB output when run:

Enqueinq 1 with correlation ID Lion


message
Enqueing 2 with correlation ID Tiger
message
Enqueing 3 with correfation ID Bear
messaqe
Enqueing 4 with correfation ID Fish
message
Enqueingr 5 with correfation ID Horse
message
Enquej-ng 6 with correlation ID Lion
messaqe
Enqueing 7 with correlation ID Tiger
messaqe
Enqueing 8 with correlation ID Bear
message
Enqueing 9 with correlation ID Fish
message
Enqueing 10 with correlation ID Horse
message
After dequeue with correlation ID Fish: Message 4
Data 1: 40 Data 2: abcdefghijklmnopqrstuvwxyz Data 3: 18-AUG-97
After dequeue with correl-ation ID Fish: Message 9
Data 1: 90 Data 2: abcdefghijklmnopqrstuvwxyz Data 3: 23-AUG-9'7
After dequeue with saved messaqe ID: Messaqe 7
Data 1: 70 Data 2: abcdefg,hijklmnopqrstuvwxyz Data 3: 2L-AUG-97

Browsing a Queue
By setting the dequeue_mode in the dequeue options to BROWSE, you can
examine the contents of a queue without actually removing the elements.
This technique can be used to search a queue for a particular message, for
example. The SearchQueue function does exactlf that:

-- Available online as part of searchq.sql


CREATE OR REPLACE FUNCTION SearchQueue (

/* Searches the queue for the first occurrence of a message with


title p_MessageTitle, and returns the message fD for that
returns NULL' */
ii.!li;;,'= "'match'
"_r:::K:;3'i; IN
p_MessageTitle VARCHAR2 )

RETURN RAW AS
692 Oracle8 PUSQL programming

v_Messaqe MessageObj;
v_Dequeueop t i ons DBMS_Ae . DEeUEUE_OPTf ONS_T
;
v_Me s s ag,eproper t i es DBMS_Ae . ME S SAGE_PROPERTI
ES_T ;
v_MsgrD RAW(16);

e_QTimeOut EXCEpTTON;
PRAGMA EXCEPTTON_INrT (e_eTimeout, _25228) ;
BEGTN
-- Loop until there are no more messages to dequeue, or we have
-- found the desired message.
BEGIN
LOOP
-- Dequeue (in browse mode) the first messagre into v_Messag.e,
-- waiting a maxi-mum of 1 second. Since we are browsing, the
-- messag.e will not be removed from the queue.
v_DequeueOptions.wait := 1;
v_DequeueOgt ione . dequeue_rnode : = DBMS_Ae . BROWSE ;
DBMS_AQ. DEQUEUE (

queue_name => p_eueueName.


dequeue_options => v_DequeueOptions,
messagejroperties => v Messageproperties,
payload => v_Messaqe,
msgid => v_MsgID);

-- Check the message titles.


IF v_Message.title = p_MessageTitle THEN
-- Found a match, return the message ID.
COMMIT;
RETURN v_MsgTD;
END IF;
END LOOP;
EXCEPTION
WHEN e_QTimeout THEN
-- End of t.he queue reached.
NULL;
END;

-- Comrnit a1r- the dequeues and return NULL, indicating no match.


' coM4fr;
RETURN NULL;
END;

The following block illustrates how to call Searcheueue:


Chapter l7: Oracle Advanced eueuing 693

-- Available online aa parE of searchq.sql


DECLARE
v_Message Messageobj;
v_Enqueueopt i ons DBMS_AQ . ENeUEUE_OPTIONS_T ;
v_Dequeueopt i ons DBMS_AQ . DEeUEUE_OPT I ONS_T ;
v_Me s s age Proper t i e s DBMS_Ae . MES SAGE_PROpERT I ES_T ;
v_MsqID RAW(16);

c_NufiMessages CONSTANT INTEGER := 10;

e_QTimeout EXCEPTION;
PRAGMA EXCEPT]ON-]NIT (e-QTimeOut, -25228) ;
BEGIN
FOR v_Counter IN 1..c_NurMessages LOOP
-- Create a message to enqueue.
v_Message :=
MessageObj('Message, ll v_Counter, v_Counter * 10,
'abcdefghijklmnopqrsturzwxyz,, SYSDATE + v_Counter) ;

-- Enqueue it with the defaul_t options.


DBMS_AQ. ENQUEUE (

queue_name => 'Sj-mpleQ, ,


enqueue_options => v_EnqueueOptions,
messageJroperties => v_Messageproperties,
payload -> v_Message,
msgid => v_MsgID);
END LOOP;

-- Commit all the enqueues.


COMMTT;

-- Search for message 4


v_MsgID := Searcheueue(,Sitrg)leer, 'MesEage 4') i
-- If found, dequeue and print.
IF v_MsgID fS NOT NULL THEN
v_Dequeueoptions.wait := 1;
v_Dequeueoptions.msgi_d : = v_MsgID;
DBMS-AQ. DEQUEUE (
queue_name =>'Simp1ee,,
dequeue_options => v_Dequeueoptions,
messagejroperties => v_Messageproperties,
payload => v_Message,
694 OracleS PUSQL programming

msgid => v_MsgID);


v_Message. print ( ,After search,) ;
ELSE
DBMS_OUTPUT . PUT_LINE ( ,Message 4 not found, ) ;
END fF;

-- Cfear the queue.


ClearQueue (,Simplee, ) ;
END;

Using Exception Queues


Messages are automatically moved to exception queues if either
of the
following conditions is true:

l.An expiration is specified as part of the message properties at


enqueue time, and the message is not dequeued before it expires.
2. The number of dequeue attempts exceeds Ihe max_retries specified
for the queue.

The following block illustrates the latter case.

-- Available online as except.sql


DECLARE
v_Message MessageObj;
v_EnqueueOpt i ons DBMS_Ae . ENQUEUE_OpTI ONS_T ;
v_Dequeueop t i ons DBMS_Ae . DEeUEUE_OPT I ONS_T ;
v_Me s s ag.eproper t j_ e s DBMS_Ae . MES SAGE_PROPERT I E S_T
;
v_MsgrD RAW(16);
v_NormalCount INTEGER;
v_ExceptionCount,INTEGER ;

c_NumMessages CONSTANT INTEGER := 3;

e_QTimeOut EXCEpTION;
PRAGMA EXCEPTION_IN]T(e-QTimeout, _25228) ;
BEGIN
-- Enqueue 3 messag.es with the exception queue set.
FOR v_Counter IN 1. . c_NumMessages LOOP
-- Create a message to enqueue.
v_Messag'e :=
MessageObj(,Message , ll v_Counter, v_Counter * 10,
,
abcdefgrhijklmnopqrstuvrlrqrz,, SYSDATE + v_Counter) ;
Chapter l7: Oracle Advanced eueuing 695

-- Enqueue it.
v_MeasageProperties. exception_queue , ExceDt,ione,
DBMS-AQ. ENQUEUE
! = i
(

queue_name =>,Simplee,,
enqueue_options => v_EnqueueOptions,
messageJroperties => v_Messageproperties,
payload => v_Message,
msgid => v_MsgfD);
END LOOP;

-- Commit all the enqueues.


COMMIT;

-- Verify that there are three messages in the normal queue, and
-- none i-n the exception queue.
SELECT COUNT(*)
INTO v_Normal_Count
FRoM aqgSimpleeTab
WHERE queue = UppER(,Simp1eq,);
SELECT COUNT(*)
INTO v_ExceptionCount
FROM aq$SinpleeTab
WHERE queue = UppER(,Exceptione, );
DBMS_OUTPUT.pUT('After initial_ enqueues, count(simple) =,
v_Normal_Count) ;
I
DBMs-ourpur.pur LTNE(', count(exception)
=, | | v_Exceptioncount) ;

-- Dequeue the first message, then rollback. This will_ rollback the
-- dequeue, and increase the attempts field j_n the message
-- properties. since the max-retries for simpleQ is 0, this wi_1]
-- move the message to the exception queue.
v_DequeueOptions.wait := 1;
DBMS_AQ. DEQUEUE (

queue_name =>,Simplee,,
degueue_options => v_DequeueOptions,
messageJroperties => v_Messageproperties,
payload => v_Message,
msgi-d => v_MsgID) ;

ROLLBACK;

-- verify that there are two messaqes in the normal queue, and one
-- in the exception queue.
SELECT COUNT(*)
fNTO v_Normal-Count
596 OracleS PUSQL programming

FROM aq$SimpleeTab
WHERE qudue = UppER('Simpleq,);
SELECT COUNT(*)
INTO v_ExceptionCount.
FROM aqgSimpleeTab
WHERE queue = UppER(,Exceptj-one, ) ;
DBMS-OUrPUT.PUT('After dequeue and rol1back, count(simple)
=, I I
v_NormalCount);
DBMS_OUTPUT.PUT_LINE(,, count(exception) =, | | v_ExceptionCount) ;

-- Now we can get the message from the excepti-on queue. Note that
-- we have to use the message rD, since the state of this messagre is
-- EXPTRED, and the dequeue wir-r not normally return any messages
-- with a state other than READy.
v_DequeueOptions.msgid : = v_MsgID;
DBMS-AQ. DEOUEUE (

queue_name =>,Exceptione,,
dequeue_options => v_DequeueOptions,
messag.eJroperties => v_Messageproperties,
payload => v_Message,
msgid => v_MsqfD);
v_Message. Print (,After exception dequeuing, ) ;

-- verify that there are tu/o messages in the normaf queue, and none
-- in the exception queue.
SELECT COUNT(*)
INTO v_Normal_Count
FROM aqgsimpleeTab
WHERE queue = UppER(,Simpleq,);
SELECT COUttrl*1
fNTO v_Exceptioncount.
FROM ag$SimpleeTab
WHERE queue = UppER(,Exceptione, ) ;
DBMS_OUTPUT.PUT(.,After exception dequeue, count(simpl-e) = , I I
y_Normal_Count ) ;
DBMS_OUTPUT. PUT_LINE (,, count (exception)
=, I I

v_Exceptj-onCount ) ;

-- Clear the queue, and commit.


ClearQueue (,Simplee, ) ;
COMMTT;
END;
Chapter l7: Oracle Advanced Queuing 597

Dropping Queues
The following script will stop all of the queues, drop them, and drop their
contai ning queue tables.

-- Availalcle online as droPq.sql


BEGfN
DBMS-AQADM. STOP-QUEUE (, SiMPICQ, ) ;
DBMS-AQADM. DROP-QUEUE (, SiMPICQ, ) ;
DBMS-AQADM. STOP-QUEUE ( 'ExceptionQ' ) ;
DBMS-AQADM. DROP-QUEUE ( ' ExceptionQ ' ) ;
,
DBMS-AQADM. DROP-QUEUE-TABLE ( SiMPICQTAb, ) ;
END;

BEGIN
DBMS-AQADM. STOP-QUEUE ( ' Priori tyQ' ) ;
DBMS_AQADM. DROP-QUEUE ( ' PriorityQ' ) ;
DBMS_AQADM. DROP_QUEUE_TABLE ( ', Priori tyqtab', ) ;
END;

DROP TYPE MessageObj;

Surnmarrf
ln this chapter, we have examined the Oracle Advanced Queuing system.
This robust implementation provides a queuing system built into the
database, ratherthan with a separate product. Thus, allof the advantages of
Oracle are supplied for Advanced Queuing. We have discussed the
operations necessary for the use and administration of queues, with their
supporting PL/SQL packages. ln the next chapter, we will examine
additional DBMS packages available with Oracle: DBMSJOB and
UTL FILE.
700 OracleS PUSQL Programming

long with the other DBMS packages that we've seen so far,
PL/SQL provides DBMSJOB and UTL_FlLE. The
DBMSJOB package, available with PL/SQL 2.2 and higher,
allows stored procedures to be run periodically by the
system, without user intervention. The UTL_FILE package,
available with PL/SQL 2.3 and higher, adds the ability to read and write to
operating system files. These packages extend PL/SQL and provide
functions that are available with other third-generation languages.

Database Jobs
With PL/SQL2.2 and higher, you can schedule PL/SQL routines to
run at specified times. This is done with the DBMSJOB package,
which implements job queues. A job is run by submitting it to a job queue,
along with parameters specifying how often the job should be run.
lnformation about currently executing jobs, and the success or failure of
previously submitted jobs, is available in the data dictionary. For more
information about database jobs, see the Server Administrator's Guide,
release7 .2 or laler.
Oracle Advanced Queuing, available with PL/SQL 8.0 and
described in Chapter 17, enhances the queuing capabilities of
PL/SQL well beyond what DBMSJOB provides.

Background Processes
An Oracle instance is made up of various processes running on the system.
Different processes are in charge of running different aspects of the
database, such as reading database blocks into memory, writing blocks
back to disk, and archiving data to offline storage. These processes are
described in Chapter 22.|n addition to the processes that manage the
database, there are processes known as the SNP processes. SNP processes
implement database snapshots, and also job queues.
SNP processes run in the background, like other database processes.
Unlike other database processes, however, if an SNP process fails, Oracle
restarts it without affecting the rest of the database. lf other database
processes fail, this generally brings down the database. Periodically, an
SNP process will wake up and check for a job. lf a joh is due to be run, the
SNP process will execute it and then go back to sleep. A given process can
be running only one job at a time. ln OracleT, there can be a maximum of
ten SNP processes (numbered SNPO through SNPg), so a maximum of ten
Chapter l8: Database Jobs and File l/O 70 I

database jobs can be running simultaneously. ln Oracle8, this limit has


been increased to 36 SNP processes, SNPO through SNPg and SNpA
through SNPZ.
Three parameters in the INIT.ORA initialization file control the behavior
of the SNP processes. They are described in Table 1B-1 . Note that if
JOB_QUEUE_PROCESSES is set Io zero, no jobs will be executed. Since
each process will sleep for JOB_QUEUE_INTERVAL seconds before
checking for new jobs, JOB_QUEUE_INTERVAL specifies the minimum
amount of time between job executions.

Default Range of
Parameter Value Values Description
JOB_QUEUE_PROCESSES 0 0..10 How many processes
to start.
JOB_QUEUE_INTERVAL 60 1..3600 lnterval between
wake-ups of the
process. The process
will sleep for the
specified number of
seconds before
checking for a new
job.
JOB_QUEUE_KEEP_ FALSE TRUE, Controls whether an
CONNECTIONS FALSE SNp process closes
any remote database
connections it makes.
lf TRUE, then all
connections will be
kept until the process
is shut down. lf
FALSE, then the
connections are kept
only as long as there
are jobs to execute.

TABLE f 8- 1. Job lnitialization Parameters


702 OracteS pUSeL programming

Running a Job
ng it to a job queue, or forcing
to a job queue, an SNp process
s job can then be run
immediately, it is run only once.

SUBMIT
A job is submitted ro the job queue with the suBMlr procedure. sUBMlr is
defined as

PROCEDU RE SU BMtT(i ob O:JT Bt NARy_t NTECER,


w,hat lN VARCHAR2,
next date lN DATE DEFAULT SYSDATE, ,,
intervallN VARCHAR2 DEFAULT NULL,
no_parse tN BOOLEAN DEFAULT FALSE);

The parameters for suBMrr are described in the following


table:
Parameter Type Description
job BtNARy INTECER Job number. When the job is
created, a number is assigned to it.
As long as the job exists, its job
number will remain the same. Job
numbers are unique across an
r nstance.

what VARCHAR2 PLISQL code that makes up the job.


Typically, this is a call to a stored
procedure.
next_date DATE Date when the job will next run.
interval VARCHAR2 Function that calculates the time at
which the job will run again.
t1o_parse BOOLEAN lf TRUE, the job code will not be
parsed until the first time it is
executed. lf FALSE (the default), the
job code is parsed when it is
submitted. This is useful if the
database objects referenced by the
job do not yet exist, but you still
want to submit it.
Chapter l8: Database f obs and File l/O 703

For example, suppose we create a procedure Templnsert with:


-- Available online as tmpins.sql
CREATE SEQUENCE temp_seq
START WfTH 1
INCREMENT BY 1;

CREATE OR REPLACE PROCEDURE Templnsert AS


BEGIN
INSERT INTO temp_table (num-col-, char-cof)
VALUES (temp-seq.nextval, TO-CHAR(SYSDATE,
'DD-MON-YY HH24 :MI :SS' ) ) ;
COMMIT;
END Templnsert;
We can have Templnsert run every 10 seconds with the following
SQL*Plus script:
-- Available online aa part of tmpins.sql
SQL> VARfABLE v_JobNr:m NUMBER
SQL> BEGIN
2 DBMS_.IOB. SUBMIT ( :v-JobNum, 'Templnsert; ' , SYSDATE,
3 'sysdate + (70/ (24*60*60))');
4 END;
5/
PLlSQL procedure successfully completed.
SQL> print v_JobNum

V JOBNUM

JOB NUMBERS The job number is assigned to the job when it is first
submitted. Job numbers are generated from the sequence SYS.JOBSEQ.
Once a job number is assigned to a job, it will never change unless the job
is removed and then resubmitted.

CAUTION
Jobs can be exported and imported, like
other database objects. This does not
change the job number. lf you try to import
a )pb whose number already exists, you will
receive an error and the job cannot be
imported. ln this case, simply resubmit the
job, which will generate a new job number.
704 OracteS pUSeL programming

CAUTION
Once the job is submitted, it will be run by
one of the SNp processes in the
background. ln order to see the results, be
sure to code a COMMTT at the end of the
job procedure.

There are three special identifiers that are valid in job


a definition, listed-
in Table 1B-2., an rN parameter, so the job can.onry read this uurr".
lob.is
next date and broken are rN oUT parameters, so the .- job
r- itr"ri.un ,;;y
them. lf we modify Templnsert as foilows
-- Available online as tmpinsl.sql
CREATE OR REPLACE PROCEDURE Temphsert
(p_NextDate IN OUT DATE) AS
v_SeqNum NUMBER;
v_StartNum NIJMBER;
V-SQLCode NUMBER;
v_Errmsg VARCHAR2 (60) ;
BEGIN
SELECT temp_seq. nextvaf
INTO v_SeqNum
FROM dual;
-- See if this is the first time we,re ca]fed
BEGIN
SELECT num cof
INTO v_StartNum
FROM temp_tabl_e
WHERE char_co] = ,Templnsert Start,;
-- We,ve been called before, so insert a new vafue
INSERT INTO temp_table (num_col, char_col)
(v-seqNum, To .HAR(sysDATE,,DD-MoN_yy
'ALUES HH24:Mr:ss, ) ) ;
EXCEPTION
Chapter !8: Database fobs and File l/O 705

WHEN NO-DATA-FOTIND THEN


-- First tirne we're called, so insert
INSERT INTO temp-table (num-col, char-col)
VALUES (v-SeqNum,'Templnsert Start') ;
END;

-- If we've been cal1ed more than 15 times, exit.


IF v-SeqNum - V-StartNum > l-5 THEN
P-NextDate := NIILLi
END IF;

COMMIT;
END Templnsert;

and submit it with

BEGIN
DBMS-JOB. SUBMIT ( :v-,JobNum,'Tellrl)lnsert (next-date) i', sysdate,
'sysdate + (5/ (24*60*60) )') ;
END;

then the job will automatically remove itself from the job queue (by setting
p-NextDate to NULL) when the sequence number is greater than 15.
Because the job can return the value of next-date and broken, a job can
remove itself from the queue when desired.
fhe what parameter is a VARCHAR2 character string. As a result, any
character literals srat should be used in the call to the job procedure should
be delimited by two single quotes. The procedure call should also be

ldentifier TyPe Description


job BINARY INTECER Evaluates to the number of
the current job.
next date DATE Evaluates to the date when
the job will next run.
broken BOOLEAN Evaluates to the job
status-TRUE if the job is
broken, FALSE otherwise.

TABLE l8-2. Job Control ldentifiers


705 OracleS PUSQL Programming

terminated with a semicolon. For example, we could call Register with the
following what string:
'Register(10006,,,MUS", 470) ;'

EXECUTION INTERVALS The first time the job will be run after the
SUBMIT call is given by the next-dafe parameter. Just before the job itself is
executed, the function given by intervalis evaluated. lf the job is
successful, the result returned by intervalbecomes the new next date. lf the
job is successful and intervalevaluates to NULL, the job is deleted from the
queue. The expression given by intervalis passed as a character string, but
should evaluate to a date. Some common expressions and their effecti are
described here:

lntervalValue Result
'SYSDATE + 7' Exactly seven days from the last
execution. lf rhe job is initially
submitted on Tuesday, then the next
run will be the following Tuesday. lf
the second run fails, and it then runs
successfu I ly on Wednesday,
subsequent runs will be on
Wednesdays.
,
N EXT-DAY(TRU NC(SYSDATE), Every Friday at noon. Note the use
,,FRIDAY")
+ ]2 of the two single quotes around the
literal 'FRIDAY' within the string.
SYSDATE + 1124 Every hour.

RUN
The DBMSJOB.RUN procedure will run a job immediately. lt is defined
with

RU N(iob lN BINARY_INTECER);

The job must already have been created by calling suBMlr. Regardless of
the current status of the job, it is run immediately by the current process.
Note that the job is not run by an SNP background process.
I

Chapter l8: Database f obs and File I/O 7 07

Broken Jobs
oracle will automatically attempt to run a job again if it fails. The job will
be run again starting one minute after the first failure. lf that attempt also
fails, the next attempt is two minutes later. The interval doubles each time,
to four minutes, then to eight, and so on. lf the retry interval exceeds the
execution interval specified for the job, the execution interval is used. once
the job fails 16 times, it is marked as broken. Broken jobs will not be run
again automatically.
You can run a broken job with RUN, however. lf that call succeeds,
then the failure count is reset to zero and the job is marked as not broken.
The BRoKEN procedure can also be used to change the status of a job. lt is
defined with

BROKEN(iob I N Bl NARY_I NTECER,


broken lN BOOLEAN,
NCXI dAtC IN DATE DEFAULT SYSDATE);

The parameters are described here:

Parameter Type Description


job BINARY_INTECER Job number of the job wl-rose status
will be changed.
broken BOOLEAN New status of the job. lf TRUE, the
job is marked as broken. lf FALSE,
the job is marked as not broken
and lvill be run next at the time
specified by next_date.
next date DATE Date at which the job will be run
next. Defaults to SYSD,qTE.

Removing a Job
A job can be removed from a jobjueue explicitlv with the REN1OVE
procedure,

REMOVE(iob I N BINARY_l NTECER);


708 OracleS PUSQL Programming

where the only parameter is the job number. lf rhe next_dare for a job
evaluates to NULL (either because the job has set iI or interval evaluates to
NULL), then the job will be removed after it has finished executing. lf the
job is currently running'when REMovE is cailed, it will be r."rnoued from
the queue after it has finished.

Altering a Job
The parameters for a job can be altered after the job has been submitted.
This is done using one of the following procedures:

PROCEDURE CHANCE(iob tN BtNARy_tNTECER,


what lN VARCHAR2,
next_date lN DATE,
interval lN VARCHAR2);
PROCEDURE WHAT(iob tN BtNARy_tNTECER,
what lN VARCHAR2);
PROCEDURE NEXT_DArEjob tN BtNARy_tNTECER,
next_datelN DATE);
PROCEDURE INTERVAL(iob tN BtNARy_tNTECER,
interval lN VARCHAR2);

The CHANCE procedure is used to alter more than one job


.
characteristic at once, and the WHAT, NEXT DATE, and tXiff nVnl
procedures are used to change the characteristic identified by their
respective arguments.
All the arguments behave the same as they do in the SUBMIT
procedure. lf you change what using GHANCE or WHAT, then the current
environment becomes the new execution environment for the job. For
more information on job environments, see the section ',Job Execution
Environments," wh ich fol lows shortly.

Viewing Jobs in the Data Diction arf


several data dictionary views record information about jobs. dbajobs and
userjobs returR information about a job, such as what, next_datel, and
interval.lnformation about the execution environment is also included. The
dbajobs-running view describes the jobs that are currently running. These
views are described in Appendix D.
Chapter l8: Databasefobs and File l/O 709

Job Execution Environments


When you submit a job to a queue, the current environment is recorded.
This includes the settings of NLS parameters such as NLS_DATE_FORMAT.
The settings recorded at job creation will be used whenever the job is run.
These settings will be changed if the what characteristic is changed using
CHANCE or WHAT.

NOTE
A job can change its environment by issuing
the ALTER 5855/ON command through the
DBMS_SQL package. lf this is done, it will
only affect the current execution of the job,
not future executions. The DBMS_SQL
package is described in Chapter 15.

File I/O
As we have seen, PL/SQL does not have input and output capability
built into the language itself, but does have this functionality
through supplied packages. l/O to the screen has been implemented with
the DBMS_OUTPUT package, described in Chapter 14. PL/SQL 2.3 extends
l/O to text files, with the UTL_FILE package. There is no way to output
directly to a binary file with this version of UTL_FlLE. This restriction will
likely be lifted in future versions of PLISQL.
OracleB allows binaryfilesto be read by using BFlLEs, which are a
special form of external LOBs. BFILEs are discussed, along with the
other LOB types, in Chapter 21.
This section describes how UTL FILE works. Three complete examples
at the end of the section demonstrate the package.

Security
Client-side PL/SQL has a package similar to UTL_FlLE, known as TEXT_IO.
There are different security issues on the clientthan on the server, however.
Files created with the client-side TEXT_IO package can be placed
anywhere on the client, subject to operating system privileges. There are
no privileges associated with PL/SQL or the database itself for client-side
file l/O.
710 OracteS pUSeL programming

urity mechanism is needed. This is


tories into which the package can
directories into which UTL lltE can
write. They are deJined by the url_FrLE_D|R parameter
in the rNrr.oRA
initialization file. Each accessible directory is indilatej
by a line ru.n u,

UTL_Fl LE_D lR = d i rectory_name

in the initialization file, The specification of directory_namewill


vary,
depending on the operating system. tf the opeiaii"l
i1","r is case sensitive,
l!:l {tr.rry-name is case sensitive. Fo,. i6" roilo*ing
that the directories"*;;;;'
lNlr.oRA are legar "*urpru]
for a Unix system, assuming
specified actual ly exist:

re UTL_FILE_DIR = /rmp
UTL_FILE_DfR = /home/oracle/output_fi1es
ln order to access a fire with url_FrLE, the directory
filename are passed as separate
----'/ name and the
he
directory name.is compaied aga
nd,
then the operation is allowed.lf
EN is
not accessible, an error is returned
tories
are not allowed, unress the subdirectory is arso risted
expricitry as
accessibre. civen the preceding ,.."*ibre directories,
Tabre 1B_3
describes legal and illegal direclory/ ilename pairi- --'

NOTE
Even if the operating system is not case
sensitive, the comparison between the
specified directory and the accessible
directories is always case sensitive.

lf the INIT.ORA file contains

UTL_FILE_DIR = * I
I
then database permissions are disabled. This
makes all directories
accessible to UTL FILE.
r9

Chapter l8: Database Jobs and File t/O T I I


r:
..).,

Directory Name Filename Comment


/tmp myfile.out Legal
/home/orac e/outp ut_f
I i I es students.list Legal
Itmpltoos january.results lllegal-subd irectory
Itmpl1995 is not
accessible
/home/oracle output_f i les/c asses. I i st
I lllegal-subd irectory
passed as part of
it
the filename
.til /TMP myfile.out lllegal-case
different

TABLE l8-3. Legal and tttegat Fite Specifications


'.:i:

rri
CAUTION
Turning off database permissions should be
used very carefully. Oracle does not
recommend that you use this option in
.:.( production systems, since it can circumvent
operating systern permissions. ln addition,
do not Ltse "." (the current directory on Unix
systems) as part of the accessible directories
list. Always use explicit directory paths.

Operating System Security


The file l/o operations that are performed by UTL_FILE wili be done as the
oracle user. (The oracle user is the owner of the files that are used to run
the database, and also the owner of the processes that make up a database
instance.) Consequently, the oracle user has to have operating system
privileges to read from and write to all of the accessible directories. lf the
oracle user does not have privileges for an accessibie directory, then any
operations in that directory will be prohibited by the operating system.
Any files created by UTL_FILE will lie owned by the oracle user and
created with the default operating system privileges for the Oracle user. lf it
712 OracleS PUSQL Programming

is necessary for other users to access these files outside of UTL_FrLE, then
the system administrator should change the permissions on the files.

WARNING
It is also good security practice to prohibit
write operations on directories in the
accessible directory list. The only user who
should be given write permission on
accessible directories should be the Oracle
user. lf'users are allowed write permission,
they can create symbolic links to other
directories, and thus circumvent operating
system privi lege checki ng.

Exceptions Raised by UTL_FILE


lf a procedure or function in UTL_FILE encounters an error, it will raise an
exception. The possible exceptions are listed in Table 1B-4. Note that these
exceptions include seven that are defined in UTL_FILE, and two predefined
exceptions (NO-DATA_FOUND and VALUE_ERROR). The UTL_FtLE
exceptions can be caught by name or by an OTHERS exception handler.
The predefined exceptions can be identified by their SQLCODE values
as well.

Opening and Closing Files


All of the operations in UTL_FILE use a file handle. fhe fite handle is a
value that you use in PL/SQL to identify the file, similar to the cursor lD in
DBMS_SQL. All file handles have the type UTL_F|LE.FtLE_TYPE. FtLE_TypE
is defined in the specification of UTL_FILE. File handles are returned
by FOPEN.

FOPEN
FOPEN opens a file for input or output. A given file can be opened for input
only or output only at any time. A file can't be used for both input and
output simultaneously. FOPEN is defined with
FUNCTION FOPEN(/ocation lN VARCHAR2,
filename lN VARCHAR2,
open_mode lN VARCHAR2)
RETURN FILE TYPE;
Chapter l8: Database f obs and File l/O 7 I 3

Exception Raised When Raised By


INVALID_PATH Directory or filename is FOPEN
invalid or not accessible.
INVALID_MODE lnvalid string specified for FOPEN
file mode.
INVALID_FILEHANDLE File handle does not FCLOSE, CET_LINE,
specify an open file. PUT, PUT_LINE,
NEW-LINE, PUTF,
FFLUSH
INVALID_OPERATION File could not be opened CET_LINE, PUT,
as requested, perhaps PUT_LINE,
because of operating NEW_LINE, PUTF,
Also
system permissions. FFLUSH
raised when attempting a
write operation on a file
opened for read, or a read
operation on a file opened
for write.
READ_ERROR Operating system error CET_LINE
occurred during a read
operation.
WRITE_ERROR Operating system error PUT, PUT_LINE,
occurred during a write NEW_LINE,
operation. FFLUSH, FCLOSE,
FCLOSE ALL
INTERNAL_ERROR Unspecified internal error. All functions
NO_DATA_FOUND End of file reached during GET_L|NE
a read.
VALUE_ERROR lnput line too large for CET_LINE
buffer specified in
CET LINE.

TABLE l8-4. Exceptions Raised by UTL_F|LE


7 l4 OracleS PUSQL Programming

The directory path specified must already exist-FopEN will not create
it. lt will, however, overwrite an existing file if the mode is,w,. The
parameters and return value for FOPEN are described in the following
table. FOPEN can raise any of the following exceptions:

T UTL_FILE.INVALID_PATH
T UTL-FILE.INVALID-MODE
I UTL FILE.INVALID OPERATION
I UTL-FILE.INTERNAL ERROR

Parameter Type Description


location VARCHAR2 Directory path where the fiie is
located. lf this directory is not
in the accessible directories
I ist, UTL_FILE.INVALID PATH

is raised.
filename VARCHAR2 Name of the file to be opened.
lf the mode is 'w', any existing
file is overwritten,
open-mode VARCHAR2 Mode to be used. Valid values
are
'r' : Read text
'w': Write text
'a': Append text
This parameter is not case
sensitive. lf 'a' is specified and
the file does not exist, it is
created with'w' mode.
return value url FILE.FILE TYPE File handle to be used in
subsequent functions.

FCLOSE
When you are finished reading from or writing to a file, it should be closed
with FCLosE. This frees the resources used by urL_FILE to operate on the
file. FLCOSE is defined wiih
Chapter l8: Database Jobs and File l/O 7 I 5

PROCEDURE FCLOSE(fi le_handle lN OUT FILE_TYPE);

where the only parameter is the file handle. Any pending changes that have
yet to be written to the file are done before the file is closed. lf there is an
error while writing, UTL_FILE.WRITE_ERROR is raised. lf the file handle
does not point to a valid open file, UTL_FILE.INVALID_FILEHANDLE
is raised.

IS OPEN
This boolean function returns TRUE if the specified file is open, FALSE if
not. lS OPEN is defined as

FUNCTION lS_OPEN(fi le_handle I N FILE_TYPE)


RETURN BOOLEAN;

There could still be operating system errors if thefile is used, even if


lS OPEN returns TRUE.

FCLOSE ALL
FCLOSE_ALL will close all open files. lt is meant to be used for cleanup,
especially in an error handler. The procedure is defined as

PROCEDURE FCLOSE-ALL;

and does not take any parameters. Any pending changes will be flushed
before the files are closed. Because of this, FCLOSE_ALL can raise
UTL_FILE.WRITE_ERROR if an error occurs during the write operation.

CAUTION
FCLOSE ALL will close the files and free the
resources used by UTL_FILE. However, it
does not mark the iiles as closed-lS OPEN
will still return TRLTE after an FCLOSE ALL.
Any read or write operations on files alter
FCLOSE ALL w'ill fail unless the file is
reopened w'ith FOPEN.
7 l5 Oracte8 pUSeL programming

File Output
Five procedures are used to
output
NEW_L|NE, PUTF, and FFLUSFi. p
very much like their counterparts
in
discussed in Chapter 14. The
maxim
1,023 bytes.

PUT
PUT will output the specified string to the specified
have been opened foi write op"rr,"i"*. pur fire. The fire shourd
is defined with
PROCEDU E pUT (fi te_hand I e
R
I N Ft LE_TypE,
buffer tN VARCHAR2);

PUT will not append a newl


PUT_LINE or NEW_L|NE to incl

3l,3PT?ti.g tyrt"r during the


ERROR is raised. The"rror
parameters for

Parameter Type Description


file_handle UTL_FILE.FILE TYPE File handle returned by
FOPEN. lf this is not a valid
handle, then UTL F|LE.
INVALID_FTLEHANDLE is
raised.
buffer VARCHAR2

raised.

NEW_LINE
one or more line terminators to
the specified file. tt
il?H"ilfinw'ires is

PROCEDU RE N EW,_L I i.i E(fi I e_h a nd I e I N Ft LE_TypE,


/ines tN NATURAL := 1);
Chapter l8: DatabaseJobs and File l/O T ll

The line terminator is system dependent--different operating systems


will use different terminators. lf there is an operating system error during
the write, UTL_FILE.WRITE_ERROR is raised. The parameters for
NEW_LINE are described here:

Parameter Type Description


file-handle urL-FILE.FILE-TYPE File handle rerurned by FopEN. tf
this is not valid, UTL_F|LE.tNVAL|D
FILEHANDLE is raised.
lines NATURAL Number of line terminators to
.1,
output. The default value is which
outputs a single newline. lf the file
was not opened in 'w' or ,a, mode,
UTL-F I LE. I NVAL I D-OPERATI ON
is raised.

PUT LINE
PUT-LINE outputs the specified string to the specified file, which must have
been opened for write operations. After the string is output, the
platform-specific newline character is output. pur LrNE is defined with

PROCE DU RE PUT_L I N E (f i I e_h and I e


I N F I LE_TYPE,

buffer lN VARCHAR2);

The parameters for PUT_LINE are described in the following table.


calling PUT-LINE is equivalent to calling PUT followed by NEW_LINE to
output the newline. lf there is an operating system error during the write,
UTL FILE.WRITE ERROR is raised.
Parameter Type Description
file_handle UTL_FILE.FILE_TYPE File handle rerurned by FOpEN. tf
this is not a valid handle,
UTL_FI LE. I NVALI D-FI LEHAN DLE
is raised.
buffer VARCHAR2 Text string to be output to the file. lf
the file was not opened in'w, or ,a,
mode, UTL_FI LE. I NVALT D
OPERATION is raised.
718 OracteS pUSeL programming

PUTF
PUTF is similar to pUT, but it ailows the output
string to be formatted.
PUTF is a limited version of the C function printfT",id
h", ,ynau* ,irnit"r,o
printf0. pUTF is defined with
PROCEDURE pUTF(file handle tN FtLE_TypE,
format lN VARCHAR2,
argl lN VARCHAR2 DEFAULT NULL,
arg2 tN VARCHAR2 DEFAULT NULL,
arg3 tN VARCHAR2 DEFAULT NULL,
arg4tN VARCHAR2 DEFAULT NULL,
arg5 tN VARCHAR2 DEFAULT NULL);

The format string contains regurar text, arong with


two speciar
characters o/"s and \n. Each o..,]r,"n." of %s ii the
formai'suing i,
replaced with one of the optionar arguments. Each
occurrence of \n is
replaced by a newline character. ThE parameters are
Jescribed in the table
after the example. As with pUT and pur_LrNE, ir,n"r"
is an operating
system error during the write, urL_FlLE.wRlrE_ERROR
is raised.
For example, if we were to execute the block

DECLARE
v_OutputFile UTL_FILE. F,ILE TypE ;
v_Name VARCHAR2(10) := ,Scott,;
BEGIN
v_OutputFile := UTL_FfLE.FOPEN(. . . ) ;
UTL_FILE . PUTF (v_OutputFi le,
,Hi there!\nMy name
is %s, and I am a ?s major.\n,.
v_Name, ,Computer Science,) ;
FCLOSE (v_OutpurFile );
END;

the output file would contain the lines

Hr 't'nere I
My name is Scott, and I am a Computer Science ma_jor..

Parameter TYPe Description


file handle UTL_FILE.FILE_TypE File handle returned by FOpEN.
tf
this is not a valid handle,
UTL-FILE.I NVALI D-FI LEHAN DLE
is raised.
Chapter l8: Database Jobs and File l/O 7 19

Parameter Type Description


format VARCHAR2 Format string containing regular
text and possibly the special
formatting character'7os' or'\n'.
lf the file was not opened in 'w'
or'a' mode, UTL_FILE.INVALID
OPERATION is raised.
argl ... arg5 VARCHAR2 One to five optional arguments.
Each argument will be substituted
for the corresponding' o/"s' format
character. lf there are more '7os'
characters than arguments, the
empty string (NULL) is substituted
for the format character.

FFLUSH
The data clutput with PUT, PUT_LINE, PUTF, or NEW_LINE is normally
buffered. When the buffer is full, it is then physically output to the file.
FFLUSH forces the buffer to be immediately written to the specified file. lt
is defined with

PROCEDURE FFLUSH(fi le_handle I N FILE_TYPE);

FFLUSH can raise any of the following exceptions:

I UTL FILE.INVALID FILEHANDLE


T UTL FILE.INVALID OPERATION
I UTL FILE.WRITE ERROR

File lnput
GET_LINE is used to read from a file, rather than to write to it. One line of
text is read from the specified file and returned inthe bufferparameter.
The newline character is not included in the return string; CET-{-tNE is
defined with

PROCE D U RE C ET-L I N E (fi I e-han d I e I N FI LE-TYPE,


buffer OUT VARCHAR2);
720 OracleS PUSQL programming

When the last line is read from the file, No_DATA_FouND is raised.
lf the line does not fit into the buffer supplied parameter,
VALUE-ERRoR is raised. Reading an empty line "i "n
will".tu"l
return an empry
string (NULL). lf an operating system error occurs during the read,
urL-FILE.READ_ERROR is raised. The maximum size o] the input line
is 1 ,O22 bytes. The parameters are described here:

Parameter Type Description


file-hahdle urL-FTLE.FTLE_TypE File handle returned by
FOPEN. tf this is not a valid
handle, UTL_FtLE. I NVALTD
FILEHANDLE is raised.
buffer VARCHAR2 Buffer into which the line will
be written. lf the file was not
opened for reading (,r,), then
UTL_FILE.INVALID OPERATION
is raised.

Examples

Debug Package
We can implement the Debug package using UTL_F|LE, as follows:

-- Availalcle online as debug.sql


CREATE OR REPLACE PACKAGE Debug AS
/* Globaf variabfes to hold the name of the debugging file and
directory. */
v_DebugDir VARCIAR2 (50 ) ;
v_DebugFile VARCHAR2 (20 ) ;
PROCEDURE Debug (p_Descriptio'n IN VARCHAR2,
p_Va1ue IN VARCHAR2);
PRoCEDURE Reset(p-NewFile IN VARCHAR2 := v-DebugFile,
p_NewDir IN VARCHAR2 := v_DebugDir) i
/* Closes the debugrging fiJ_e. */
PROCEDURE Close;
Chapter l8: Database fobs and File l/O 721

END Debug;

CREATE OR REPLACE PACKAGE BODY Debug AS


v_DebugHandle UTL_FILE. FILE_TYPE ;

PROCEDITRE Detrug (p_Deseription TN-RCHAR2,


D_Value IN VARCIIAR2) IS
BEGIN
/* Output the info, and fl-ush the fi1e. */
UTL_FILE.PUTF(v_DebugHandle,'Bs: %s\n',
p_Description, p_Va1ue) ;
UTL-FILE . FFLUSH ( v-DebugHandle ) ;
EXCEPTION
WHEN UTL_FILE . INVALID_OPERATION THEN
RArSE_APPLfCATrON_ERROR ( -2 0 1 02 ,

'Debug: Invalid Operation, ) ;


WHEN UTL_FILE. INVALTD_FILEHANDLE THEN
RArSE_AppLrCATrON_ERROR ( - 2 0 1 0 3,
'Debugr : Tnvalj-d File Handle, );
WHEN UTL-FTLE.WRTTE-ERROR THEN
RAT SE-APPLTCATION_ERROR ( - 2 O 1 O 4,
'Debug: Write Error, ) ;
END Debug;

PROCEDITRE Reset(D_lilewFile rN VARCHAR2 := v_DetrugFile,


p_NewDir IN VARCI1AR2 := v_DebugDir) IS
BEGTN

/* Make sure the file is closed first. */


IF UTL_FILE. IS_OPEN (v_DebugHandle) THEN
UTL_FTLE. FCLOSE (v_DebugHandle ) ,.

END IF;
/* Open the fil-e for writing. */
v_DebugHandl-e := UTIJ_FILE.FOPEN(p_NewDir, p_NewFi1e,,w, ) ;

/* Set the packaged variables to the values just passed in. */


v_DebugFj-lg3 = p_NewFile;

v_DebugDir 3= p_NewDir;
EXCEPTION
WHEN UTL_FTLE. INVALTD_PATH THEN
RAISE_APPLICATION_ERROR(-2O1OO,,Reset: Invalid PaIh, ) ;
WHEN UTL-FILE. INVALID_MODE THEN
RAISE_APPLICATTON_ERROR(-20101,'Reset: Invalid Mode, ) ;
WHEN UTL-F]LE. INVALfD_OPERATION THEN
722 OracleS PUSQL Programming

RAISE_APPLICATION_ERROR(-20101,,Reset: Inva]id Operation, );


END Reset;

PROCEDITRE Close IS
BEGIN
UTL_FILE . FCLOSE (v_DebugHandle ) ;
END Close;

BEGTN \

v_DebugrDir:='/tmp';
v_DebugFile :='debug.out' ;
D^-^l .

END Debug;

This version of Debug behaves the same as the other versions we have
examined in Chapters 13 and i 4, with some minor changes. Debug.Reset
takes the name and location of the debug file as parameters-if they are not
specified, the output file defaults to /tmp/debug.out. All debugging
statements will add a line to this file. We've also added a new procedure,
Debug.Close. This procedure should be called to close the debugging file.
Although Debug.Debug flushes the output file, the file slrould still be
closed to free the resources associated witlr it.

TIP
Note the exception handlers for the various
routines. They identify which errors were
actually raised, and by which procedures.
This is a good technique to follow when
using UTL_FlLE. Otherwise, you would have
to trap errors with a WHEN OTHERS
handler.

Student Loader
The LoadStudents procedure will insert into students based on the
'contents of the file that is passed to it. The file is comma delimited,
which means that each record is contained on one line, with commas used
to separate the fields. This is a common format for text files. LoadStudents
is created with:
-- Available online as loadstud.sql
CREATE OR REPLACE PROCEDURE LoadStudents (
Chapter l8: Database f obs and File l/O 723

/* l,oads che students table by reading a comma-delimited file.


The file should have 1i-nes that look like:

first_name, l-ast-name, maj or

The student ID is generated from student-sequence.


The total number of rows inserted is returned by
p_TotalInserted. */
p_FileDir IN VARCIIAR2,
p_FileName IN VARCHAR2,
p_Totallnserted IN OUT NI/MBER) AS

v_Fif eHandle UTL_FILE. FILE-TYPE ;


v_Newl,ine VARCHAR2 1100) ; -- Input line
v_FirstName students . f irst_name%TYPE ;
v_LastName students . last-namegTYPE ;
v-Maj or students.maj or%TYPE;
/* Positions of commas within input 1ine. */
v_FirstComma NUMBER;
v_SecondComma NUMBER;

BEGIN
-- Open the specified fife for reading.
v_FileHandle : = UTL_FILE. FoPEN (p-Fi1eDir, p-F'ileName , 'r' );

-- Initialize the output number of students.


p-Totallnserted : = 0;
-- Loop over the file, reading in each lj-ne. GET-LINE will
-- raise NO_DATA_FOUND when it is done, so we use that as the
-- exj-t condition for the loop.
LOOP
BEGIN
UTL_FILE . GET-LINE ( v-F i 1 eHandle, v-Newline ) ;
EXCEPTION
WHEN NO DATA FOUND THEN]
EXIT;
END;

-- Each field in the input record is delimited by commas. We


-- need to find the locations of the two conmas in the line
-- and use these locations to get the fields frdm v-Newline.
-- Use INSTR to find the locations of the commas.
v_FirstComma := INSTR(v-NewLine, ',', I, 7);
v_secondCornma := INSTR(v-NewLine, ' ,' , 1, 2);
724 OracteS pUSeL programming

-- Now we can use SUBSTR to extract the fields.


v_FirstName := SUBSTR(v_NewLine,
1, FirstComma _ 1);
v-LastName := suBSTR(v-NewLine, v_FirstComma
+ 1.
v_Major := sUBSrR,"_r];iiillTlHl.;":#:":";ila - 1) ;

-- fnsert the new record into students.


fNSERT fNTO students (fl, first_name,
fast_name, major)
VALUES (student_sequence. nextval,
v_firstlf+n.;
v_LastName, v_Major);
p_Totalfnserted := p_Totalfnserted +
1;
END LOOP;

-- Close the file.


UTL_FrLE. FCLOSE (v_Fi leHandle
);

COMMTT;
EXCEPTTON
-- Handle the UTL FILE exceptions meaningfully,
-- that the file is properly closed. and make sure
WHEN UTL_FILE. TWAITD-OPERATTON THEN
UTL_FrLE. FCLOSE (v_FileHandle)
;
RAISE_APPLTCATION ERROR (
-20 051,,

urL_FrLE. rNVAr: D_FrLE;*;fftHfft":


rnvarid operarion' ) ;
'HEN
UTL_FILE. FCLOSE (v_FileHandle
);
RATSE-APPLICATTON ERROR (
-20 052,

*HEN
rnvalid File Handle,)
iRR.R
''L_FTLE.READ
UTL_FTLE. '";;"utt"uents:
FCLOSE (v_FileHandle );
RAfSE_APPLICATION ERROR ( -2 0053,
LoadStudents: Read Error, ) ;
WHEN OTHERS THEN
UTL_FILE . FCLOSE (v_FileHand]e ) ;
RAISE;
END
a
LoadStudents;

A sample input fire for Loadstudents courd


rook rike:
scott, smith, Computer Science
Margaret, Mason, History
,Joanne,,Junebug, Computer Science
Manish. Murgratroid, Economi cs
Chapter l8: Database Jobs and File l/O 725

Patrick, Po11 , History


Tirnothy, Tal 1er, History
Barbara, Blues , Economics
David, Dinsmore, Music
Ester, Elegant, Nutri tion
Rose, Rj-znit, Music
Rita. Razmataz , Nutrition

NOTE
LoadStudenfs uses the student_sequence
sequence number to determine the student
lD. lf this sequence has not been initialized,
it could return a value which is already in
students. ln this case, the primary key
constraint for students would be violated.

Printing Transcripts
We first saw PrintTranscript in Chapter 13. Now that we know how to use
UTL_FILE, we can complete the procedure. We first need the Calculate
GPA procedure:

-- AvaLlable online aa calccPA.sql


CREATE OR REPLACE PROCEDURE CAICU1AICGPA (

/* Returns the grade point average for the student identified


by p_StudentlD in p_GPA. */
p_studentlD IN students. IDSTYPE,
p_GPA OUT NI,JI{BER) AS

CURSOR c ClassDetails IS
SELECT classes.num_credits, rs. grade
FROM classes, registered-students rs
WHERE classes.department = rs.department
AND classes. course = rs. course
AND rs.student_id = p_StudentlD;

v-NumericGrade NUMBER;
v_TotalCredits NUMBER := O;
v_TotalGrade NUMBER := 0;

BEGIN
FOR v ClassRecord in c ClassDetails LOOP
-- Determine the numeric value for the grade.
726 Oracle8 PUSQL Programming

SELECT DECODE(v_ClassRecord.grade,, A', 4,


'8,, 3,
,C' , 2,
,D' , I,
'E' , 0)
INTO v_NumericGrade
FROM dual_;

v-Totalcredits := v_Totalcredits + v_classRecord.num_credi_ts;


v_TotalGrade := v Totalcrade +
(v-Classnecord. num-credits * v-NumericGrade) ;
END LOOP;

p_GPA := v_TotalGrade ,/ v_TotalCredits;


END CalcufatecpA,

PrintTranscript is created with:

-- Awailable online as printran.sql


CREATE OR REPLACE PROCEDURE prj_ntTranscript (
/* Outputs a transcript for the indicated student. The
transcript will consist of the classes for which the
student is currently registered and the grade received
for each class- At the end of the transcript, the student,s
GPA is output. */
p_StudentlD IN students. IDtTypE,
p_FileDir IN VARCHAR2,
p_FileName IN VARCHAR2) AS

v_StudentGPA NUMBER;
v_StudentRecord studentstROWTypE;
v_FileHandle UTL_FILE . Ff Lr'._TypE;
v_NumCredits NUMBER;

CURSOR c_CurrentClasses IS
SELECT *
FROM registered students
WHERE student_id = p_StudentlD;
BEGIN
-- Open the output fife in append mode.
v_FifeHandle := UTL_FILE.FOPEN(p_FifeDir, p_Fi1eName,,w, I ;
SELECT *
INTO v_StudentRecord

\.
Chapter l8: Database Jobs and File !/O 727

FROM students
WHERE 1p = p_StudentID;

-- Output header information. This consists of lhe current


-- date and time, and information about this student.
UTL-FILE.PUTF(v-FileHandle,'Student ID: Bs\n',
v StudentRecord.ID);
UTL-FILE.PUTF(v-FiteHandle,'student Name: ?s %s\n',
v-StudentRecord. f irst-name, v-StudentRecord' last-name) ;
UTL-FILE.PUTF(v-FileHandle,'Major: ?s\n',
v-studentRecord. maj or ) ;
UTL-FILE.PUTF(v-Fi1eHand1e,'Transcript Printed on: *s\n\n\n',
TO-CHAR(SYSDATE,'Mon DD,YYYY HH24:MI:SS' ) ) ;

UTL-FILE. PUT-LfNE (v-FlfeHandle, 'Class Credits Grade' ) ;


UTL-FfLE . PUT-LINE (v-Fi feHandle, - ---- ' I ;
FOR v_CfassesRecord in c-Currentclasses LOOP
-- Determine the nurnber of credits for this c1ass.
SELECT num-credits
INTO v NumCredits
FROM c]asses
WHERE course = v-CLassesRecord.course
AND department = v-ClassesRecord.department;

-- Output the info for this class.


UTL_FILE.PUTF(v-Fj-leHandle,'8s ts %s\n',
RPAD(v-CfassesRecord.department | | I I

v-Cl-assesRecord.course, 7 ),
LPAD (v-NumCredits, 7),
LPAD (v-CfassesRecord. grade, 5) ) ;
END LOOP;
-- Determine the GPA.
CalculateGPA (p-StudentID, v-StudentcPA) ;

-- Output the GPA.


UTL-FILE.PUTF(v-FileHandl-e.'\n\nCurrent GPA: ts\n',
TO_CHAR (v-StudentcPA, '9 .99' ) ) ;

-- Cfose the file


UTL_FILE . FCLOSE (v-FileHandle ) ;

EXCEPTION
-- Handle the UTL-FILE exceptions meaningfully, and make sure
-- that the fife is properly closed.
728 Oracte8 PUSeL programming

!'JHEN UTL-FILE. TNVALTD_oPERAT]ON THEN


UTL_FrLE . FCLOSE (v_FileHandle ) ;
RAf SE_APPLTCATION_ERROR ( - 2 O O 6 1,
, printTranscrj_pt : fnvalid Operation, ) ,.
WHEN UTL_FILE. TNVALTD_FILEHANDLE THEN
UTL_FILE. FCLOSE (v_FileHandle ) ;
RAISE_APPLICATION_ERROR ( -2 OO 62 ,
,printTranscript: fnval_id Fil-e
Handle,);
WHEN UTL_FILE. WRTTE_ERROR THEN
UTL_FILE. FCLOSE (v_FileHandle ) ;
RAISE_APPLICATION_ERROR ( -2 OO 6 3 ,
,printTranscript: Write Error, ),.
WHEN OTHERS THEN
UTL_FfLE . FCLOSE (v_FileHandle );
FAISE;
END printTranscript;

lf the registered students table looks like:

SQL> select * from registered students;


STUDENT-ID DEP COURSE G

10002 cs to2 B
10002 Hrs 101- B
10002 ECN 203 A
10002 cs 101 A
10009 Hrs l-01 D
10009 MUS 410 B
10009 Hrs 301 c
10009 MUS 410 B
8 rows selected.
and we call Printrranscript for students 10002 and 10009, we get the
following two output files:

ffi Student ID: 10002


Student Name: .foanne ,Junebug
Major: Computer Science
Transcript printed on: Jan 27 ,tgg6 I7 z3j:43

Class Credits Grade


Chapter l8: Database f obs and File l/O 729

CS IO2

TS 101 4 B
ECN 203 3 A
cs 101 4 A

Current GPA: 3.41


Student ID:10009
Student Name: Rose Riznit
Major: Music
Transcript Printed on: Jan 27,1,996 17:38:56

Class Credits Grade

Hrs 101 4 D
MUS 410 3 B
HIS 301 4 C
MUS 410 3 B

Current cPA: 2.!4

Summarl
We have examined two more utility packages in this chapter: DBMSJOB
and UTL_FILE. Database jobs allow procedures to. be automatically run by
the database at predefined times. UTL_FILE adds file l/O capability to
PLiSQL, subject to security issues on the server. Each of these utilities
provides usefulfunctionality that is not inherent in the language.
732 OracleS PUSQL Programming

racle's WebServer software provides more functionality than


a traditional web server. lntegrated with the Oracle
database, it allows a PL/SQL stored procedure to generate
HTML pages. Thus you can create dynamic web pages
based on both user input and database information. ln this
chapter, we will examine the structure of the Oracle web server and how
PL/SQL works with it, and we will look at the PL/SQL data structures
required. We will also discuss how SQl-Station Coder can be used to
execute PL/SQL over the web.

The WebSen/er Envi ronment


NOTE
All of the discussion in this chapter,
including the examples, is written for and is
tested with version 2.1 of the Oracle web
server. A trial version of the web server for
Solaris and Windows NT systems is
available for download from
http://www.oracle.com and is also on the
CD accompanying this book. As of this
writing, version 3.0 of the Oracle web
server (known as the web application server)
is also available. The discussion in this
chapter is also applicable for version 3.0.

The Oracle web server is more complicated than traditional web servers
and also more powerful. Here, we will examine the components of the web
server and how they interact with each other and the Oracle database. A
full discussion of the web server and the interfaces to it is beyond the scope
of this book. For more information on administering the web server and more
.details, see the online documentation available when you install the server.
The components of Oracle's WebServer program are illustrated in
Figure 19-1. The server consists of the following components, which are
listed below and described in the following sections:

I Web listener
I CCI (Common Catewav lnterface)
Chapter l9: Oracle's WebServer program
73 3

I Web Request Broker (WRB) interface


I WRB dispatcher
I PL/SQL Agent

wEB LISTENER The web listener is responsible for receiving a URL


(Uniform Resource Locator) from the client browser and
sending"back the
appropriate output. lt does this by processing the requested URL to
determine which additional component of the web server is best suited to
creating the desired output. For example, the output could be a text file
stored on the operating system, or it could be the output from a program.
Or, the output could be retrieved from the WRB interface.

coMMoN GATEWAY TNTERFACE The purpose of a web server is


to service requests from a client browser. The responses typically are HTML
pages. The ccl (common cateway lnterface) provides an'interface
for a
pro8ram running on the server to generate the HTML as output, rather
than

Web Listener

CCI
r Executable aF,-b I f wd-l
Programs
Er'."'J t"''ry,
wRB^y//
\ener
Other Cartridges

FIGURE l9- l. The components of Oracle,s WebServer


734 OracleS PUSQL Programming

simply reading a static text file on the operating system. With CCl, the web
pages can be created dynamically. The CCI is often used to implement
HTML forms.

WEB REQUEST BROKER INTERFACE The web request broker


(wRB) interface is an additional interface that enables the web listener to
call an executable program. When the listener detects that the WRB
interface is needed, it hands processing to the wRB dispatcher and returns
to processing incoming requests. The WRB interface is specific to Oracle,s
WebServer, while the CCI is a component of all web servers.

\A/RB DISPATCHER The WRB dispatcher routes an incoming WRB


request with the aid of a set of processes known as WRB executable
engines (WRBX). A WRBX communicates with a back-end application
known as a cartridge. A cartridge is a specialized application to implement
a desired task, which communicates with the WRBX (and thus eventually to
the client through the WRBX and listener), using an open WRB APl. The
dispatcher is also specific to WebServer.

PUSQL AGENT The PLISQL agent is the final link in the process
between a client browser and the oracle server. The agent executes a call
to an Oracle stored procedure. This procedure creates a dynamic HTML
page as output, and the agent passes the output back to the client through
the listener. The agent can use either the CCI or WRB interface.

The PL/SQL Agent


The PL/SQI- agent serves as the primary means of accessing an Oracle
database. When the web listener receives a URL specifying that the PLlSeL
agent should be called, the agent will establish a connection to the
database and call a PL/SQL stored procedure. lnformation can be passed to
the procedure through its parameters, and it can include information
specified in the URL or information generated from an HTML form. The
procedure in turn generates HTML output through a set of specialized
packages that are provided as part of the web server distribution.

The Database Connection Descriptor


ln orderto callthe stored procedure, the PL/SQL agent must establish a
connection to the database. ln order to do this, it must know informatlon
Chapter l9: Oracle's WebServer Program 73 5

such as the user name and password, along with the database SID or
SQL*Net connect string. This information is stored in a database
connection descriptor (DCD). The fields contained in a DCD are described
in Table 19-1.
You can administer DCDs from the web server Administration page
created when you installthe web server. This allows you to create and
modify DCDs online. Or you can directly modify the fields in a DCD by

Field Description
username Oracle user to which to connect. Any stored
procedures called with this DCD must be owned by
this user.
password Password for this user.
ORACLE HOME Root for the Oracle software on the server's
operating system.
ORACLE SID Database SlD, if the DCD specifies a local database.
SQL*Net V2 SQL*Net version 2 connect string if the DCD
connect string specifies a remote database.
owa-error-page Absolute path to an HTML page, which the web
agent can return in case of an operating system or
database error. Note that this is not a virtual path for
the web server-it is a direct operating system path.
owa_valid-ports List of valid ports on which the listener will respond.
Different ports can be specified in the URL.
owa_log_dir Log directory where the PL/SQL agent will write a log
file. This file can be used to diagnose errors, as well
as log activities
owa-nls-lang NLS-LANC environment for the Oracle session. This
is specified in the same format as it would be for a
standard Oracle connection, such as AMERICAN-
AMERICA.USZASCII. For more information on
NLS LANC, see the Oracle Server Reierence.

TABI-E l9- l . Database Connection Descriptor Fields


735 OracleS PUSQL Programming

editing the configuration file on the server file system. For more information
on DCDs and these files, see Oracle's WebServer documentation.

CGI vs. WRB


The PL/SQL agent can be called with either the CCI or WRB interface.
Either way, the DCD is used to indicate the database connection
information. Which interface is better? with the WRB interface, the WRBX
establishes the connection in two stages:

l. The connection itself is established. The WRBX does this as soon as


it is created.
2. For each request, the WRBX will log on to the database. When
the request is complete, the WRBX logs off. The connection
remains active.

With the CCI interface, however, the PL/SQL agent must complete both
stages of the connection for each request. This is necessary because each
CCI request will spawn a new process, which must begin anew. But the
WRBX process remains alive, which means that only stage 2 is required.
since stage 1 is the most time consuming, using the WRB interface is
significantly faster and is thus recommended.

Specifying Procedure Parameters


A PL/SQL procedure can be called through the web agent using either the
cET or the Posr method. with the cET method, the parameters are passed
directly in the URL. The Posr method is used when the procedure is the
target of an HTML form. The web agent will automatically determine which
method is used and call the procedure appropriately. We will see an
example of the cET method in this section. The posr method is used
automatically with HTML forms, and is described later in this chapter.
Consider the following stored procedure:

-- Available online as heIIo.sq1


CREATE OR REPLACE PROCEDURE he].lo(p-GreetJ-ng IN VARCFIAR2) as
BEG]N
HTP . htmfOpen;
HTP. headOpen;
HTP.Ticle('Heffo Worfd!' );
Chapter l9: Oracle's WebSerryer program
Tll

HTP. headClose;
HTp. bodyopen;
HTP. print (p_Greeting) ;
HTp. bodyClose;
HTP . htmlc1ose,.
END he11o;

The calls to the HTP package will generate the actual HTML code, as
described in the next section, "pVSeL Web Toolkit.,, ln order to call this
procedure from a URL, the following steps have to be done:

l. Create the procedure in a particurar schema. suppose that the


database user is example in this case.
2. Create a DCD for the web listener that specifies exarnple as the user
lD. Suppose that is also called example.
3. Create a WRB that specifies the virtual path /example/owa as a call
to the Oracle web agent.

We can now call hello by using a URL similar to the following,


}:Ltp | / / ho s t : po r t / exampl e / owa / he1 I o ?p_Greet ing=Vr/slsems r

where host and port arc the machine and port on which the listener is
running' The listener will parse the URL given to it and will direct further
processing based on the components of the URL, each of which can
be
retrieved with the appropriate CCI environment variable. This is described
in the following table:

CGI Environment
URL Component Variable Explanation
/example/owa SCRIPT NAME This tells the listener that the
DCD example should be
used. Because the WRB
specified /example/owa as a
virtual path, the web agent
will process the remainder
of the URL.
738 OracleS PUSQL Programming

CGI Environment
URL Component Variable Explanation
hello PATH rNFo This identifies the procedure
to be called. The web agent
will expect this procedure to
produce HTML output using
the web toolkit packages.
p-creering= QUERY_STRINC This identifies the procedure
Welcome! parameters, if specified.
Note that the name of the
parameter is specified along
with the value. lf there is
more than one parameter,
they should be separated by
question marks.

When we call hello from this URL in a web browser, the output is
displayed in Netscape Navigator 3.0 as shown in Figure i9-2.rhe HTML
that hello generates is as follows:

<HTML>
<HEAD>
<TITLE>He11o Worfd I< /TfTLE>
</HEAD>
<BODY>
Welcome !

< /BODY>
< /HTML>

NOTE
Cenerated HTML pages, like any HTML
page, can be vielwed in a variety of different
browsers. Figure 19-2, for example,
uses Netscape Navigator version 3.0.
Other figures in this chapter show
different browsers.
Chapter l9: Oracle's WebServer program
Tlg

FIGURE l9-2. Output fromhello in Netscape Navigator

PUSQL Web Toolkit


The PLlsQL web toolkit is a set of packages that enable a pl/sel procedure
to generate HTML output. The packages in the toolkit are described in
Table 19-2.|n the following sections, we will see examples of many of
the-se packages. For more information on the packages, including
definitions of the subprograms, see the pLlSeL Web"Toolkit Refelence,
which is available online when you install WebServer.

NOTE
The purpose of the web toolkit is to generate
HTML. We will examine the toolkit and the
HTML it generates. We will not, however,
discuss the meaning of the HTML code
itself. For more information on HTML, see
the O rac I e Webserver doc u me ntat i on.
7 40 OracteS pUSeL programming

Package Description
HTP Hypertext procedures. This package contains
procedures that generate many.Jrron
HTML tags.
HTF Hypertext functions. This package contains
the
same functionality as HTp, but t'he subprograms
are
implemented as functions rather than procedures.
OWA* Th,is package contains internal procedures
that are
called by.the PLlSQl- agent itseif. They are
not
meant to be called externally.
OWA UTIL Useful utility functions for manipulating
dates and
dynamic SQL, as well as getting the vaiue
of CCI
environment variables.
OWA_OPT-LOCK* Allows you to impose optimistic locking
strategies
to prevent lost updates.
OWA-PATTERNX Functions that implement regular expression
pattern
matching.
OWA TEXT* Procedures, functions, and datatypes
that allow you
to manipulate large text strings. OWn_pnfTERN
uses OWA_TEXT internal ly.
OWA IMACE Utilities for manipulating server_side image
maps.
OWA-COOKIE Utilities for manipulating HTML cookies.

TABLE l9-2. Supptied packages in the pL/SeL Web Tootkit


* These packages
are not described in detail in the following
sections; see the online documentation
for more information and examples.

HTP and HTF


These two packages form the basics
consists only of procedures. Each call
one HTML tag. When the
will be returnedas part of
other hand, contains only
Chapter l9: Oracle's WebServer program
T 4l

HTP procedures, except that they return a string with the tag. This string
could then be output with HTP.PRINT, if desired.
The contents of the HTP and HTF functions can be broken into
categories, which are listed in Table 19-3. ln the following sections,
we will examine a sample from each of these categories. For complete
documentation on the syntax and parameters for eich of the funciions,
see the online documentation for the Oracle Web Toolkit.

Printing
The printing procedures are contained in the HTp package only, not HTF.
The main procedure is PRINT, which is used to output aline oi HTML
code. lts argument is passed back through the listener to the client as part
of the page. lt is overloaded with the following definitions:

PROCEDURE HTP.PRtNT(cbuf tN VARCHAR2);


PROCEDURE HTP.PRtNT(dbuf tN DATE);
PROCEDURE HTP.PRINT(nbuf tN NUMBER);

where cbuf, dbuf, and nbuf contain a value to be output. rf you pass a date
or number, it will be converted to a character string using the default
format. PRINT will output a newline character after the string passed. The
other procedures in this category (P, PRN, pRlNTS, ps) function similarly.
PRN doesn't include the newline, and pRTNTS can substitute for special
characters. The PrintDemo procedure illustrates how to use pRINT:

-- Avdilable online as prntdemo.aql


CREATE OR REPLACE PROCEDURE printDemo AS
BEGTN
-- This procedure uses PRINT to output al_l of the HTML.
-- The HTp and HTF packages contain utility procedures which can
-- accomplish much of the same functionality.
HTP.print ('<HTML>, );
HTP.print('This is a demonstration of the ,);
HTP.print ('<STRONG>HTp.prj-nt</STRONG> procedure. It can be , ) ;
HTP.print(,used to output additional_ tags that are not in,);
HTP.print('HTP and HTF, such as the ,);
HTP.print ( ,<BLINK>BLINK</BLINK> tag. ,) ;
OWA_UTfL. signature (,erintDemo, ) ;
HTP.print (, </HrlAl> ; ;
END PrintDemo;
7 42 OracleS. PUSQL Programming

Category Subprograms
Printing* p, print, prn, prints, ps
Constants bodyOpen, bodyClose, htmlOpen, htmlClose,
headOpen, headClose
Head base, isindex, linkrel, linkrev, meta, title
Body address, anchor, anchor2, area, base, basefont,
bgsound, big, blockquoteOpen, blockquoteClose, br,
center, centerOpen, centerClose/ comment, dfn, div,
fontOpen, fontClose, header, hr, img, img2, line,
listingOpen, listingClose, mailto, mapOpen,
mapClose, nl, nobr, para, paragraph, plaintext,
preOpen, preClose, s, small, strike, sub, sup, wbr
List di rl istClose, d i rl istOpen, d I istClose, d I istDef,
dlistOpen, dlistTerm, listHeader, listltem,
menulistClose, menulistOpen, olistClose, olistOpen,
ulistClose, ulistOpen
Character format cite, code, emphasis, em, keyboard, kbd, sample,
strong, variable
Physical format bold, ital ics, teletype
Form formOpen, formClose, formCheckbox, formHidden,
formlmage, formPassword, formRadio, formReset,
formSubmit, formText, formSelectOpen,
formSe ectOption, formSe lectC lose, formTextarea,
I

formTextarea2, formTextareaOpen,
formTextareaOpen 2, formTextareaC ose I

Table tableOpen, tableClose, tableCaption, tableRowOpen,


tableRowClose, tableHeader, tableData

TABLE l9-3. Subprograms in the HTp and HTF packages

+ The printing subprograms are available as procedures in the HTP package only. All other
subprograms are available as either procedures (HTp) or functions (HTF).
Chapter l9: Oracle's WebServer Program 7 43

PrintDemo generates the following HTML:


<HTML>
This is a demonstration of the
<STRONG>HTP.prj-nt</STRONG> procedure. It can be
used to output additional tags that are not in
HTP and HTF, such as the
<BLINK>BLINK< /BLINK> tag.
< /HTML>

NOTE
PrintDemo contains a call to OWA UTlt.
signature. This procedure prints a line at the
bottom of the paige with the current date,
along with a link to view the PL/SQL source.
For more information, see the section
"OWA_UTIL" later in this chapter.

Constants
There are four constants in the HTF package, which are described in the
following table. There are also equivalent HTP procedures, which will
print them.

Constant HTML Output Description


htmlOpen <HTML> Begins an HTML page. This should
be the first tag on every page (except
for comments).
htmlClose </HTML> Ends an HTML page. This should be
the last tag on every page.
headOpen <HEAD> Opens the header of the page.
' Although the header is optional, it is
good HTML format to include one.
headClose </HEAD> Closes the header.
bodyOpen <BODY> Opens the body.
bodyClose </BODY> Closes the body.
744 OracleS PUSQL Programming

Head
The subprograms in this category generate tags that are valid in the header
of an HTML page. As such, they must appear between the HTP.headOpen
and HTP.headClose calls. These tags include information such as the title of
the page. The BodyDemo example in the next section illustrates some of
the head tags.

Body
Body tags are used in the main portion of the HTML page. lt is good style to
enclose the body in <BODY> and </BODY> tags, which are generated by
HTP.bodyOpen and HTP.bodyClose. The subprograms in this category can
do things such as insert images, anchors, and listings. All of these calls can
be found in both the HTP and HTF packages. The BodyDemo procedure
illustrates several of the body tags:

-- lvailable online as bodydemo.sql


CREATE OR REPLACE PROCEDURE BodyDemo AS
BEGIN
HTP . htmlOpen;
HTP. headOpen;
HTP. title ( 'eody tags demo' ) ;
HTP . headCfose;

HTP. bodyOpen;
HTP.print('This page demonstrates several of the tags ,);
HTP.print('avai-l-able for formatting the text in the body ,);
HTP.print('of a web page, and for inserting additional ,);
HTP.print('tags.' | | Hrr.para) ;
HTP.header(2,'Links' ) ;
HTP.pri-nt('Here are some links to check out. These are ,);
HTP. print ( 'generated using the HTP. anchor procedure . , ) ;
HTP. para;
HTP. anchor \' / /www . oracl-e . com/ ' , 'Oracle Corporation, ) ;
HTP . para;
HTP. anchor (' / /www . osborne. com/oracle/ index. htm, ,
'OracIe Press' ) ;
HTP. para;
HTP.anchor ('/ /www.platinum. com/ ' , 'pl-at.inum Corporation, ) ;
HTP . para;
. Chapter l9: Oracle's WebServer program
I45

HTP.header(2,,Formats, ) ;
HTP.print(HTF.centeropen | | ,fhese lines are centered, using ,);
END Lr.

HTP.print ( 'the HTF. centeropen and HTF. centerClose functions. , ) ;


HTP. centerCl_ose;

HTP.strike(,Here,,s some strikethrough text., ) ;


OWA_UTIL. signature ( , BodyDemo' )
HTP . bodyCJ_ose;
HTP. htmlClose;
END BodyDemo;

The HTML that BodyDemo generates is given next, and the page itself
(in Netscape Navigator 4.0) is shown in Figure 19-3.

<HTML>
<HEAD>
<TfTLE>Body tags demo< /TITLE>
< /HEAD>
<BODY>
This page demonstrates several of the taqs
availabl-e for format.ting the t.ext in the body
of a web page, and for inserting additional
tags . <P>
<H2>Links</H2>
Here are some links to check out. These are
generated using the HTp.anchor procedure.
<P>
<A HREF= " / /vww . oracle. com/ ,,>Oracle Corporation</A>
<P>
<A HREF- " / / www . osborne . com/oracle / index. htm" >Orac1.e press< /A>
<P>
.O " / /www. platj-num. com/ ,'>platinum Corporation< / A>
<P> "*"U=
<H2>Format s< /H2>
<CENTER>These l_ines are centered, using
<BR>
the HTF.centeropen and HTF.centerclose functions.
</CENTER>
<STRfKE>Here,s some strikethrough text.</STRIKE>
</BODY>
< /HTML>
7 46 OracleS PUSQL programming

Thb p.g! d6@r@.r 3*d.l of thr tag, rmil.bl. fd foddiaB th. tdir thc bod5r of a web
pig., ed fd i@.tting.ddition l Eg..

f .hkg

Hm d. ,m.lirib lo chrck out Th.aa @ tarBrdrdwintthc HTp ehorptocadw.

Orulc Cftprat@

Oracla Pre55

Plaliqh Cmdatim

Formets
Th.r. Iiru. dc c.d!.r.4 uriag
tb. HTF cciltoF@ ddlffF.cffitcloe; i&cti@s
H-d..-.ddLthldb+.t*

fbir l.g. r-s protu4d OrL


by rh! yftD Ag.d @ July 17, t99t &J2 pM
YicwPL/SOL sowcc code

FIGURE I 9-3. Output from BodyDemo in Nerscape Navigator

List
There are of lists available in HTML. Each list consists
of a set of ine, with a formafting tag (such as a bullet or
number) i svailable lists are de"scribed here:

I An ordered /rst has numbered items.


t An unordered listhas bullets.
r A menu /ist is similar to an unordered list, but is usually presented
in a more colnpact form.
r A definition /rst includes an item, foilowed by a definition.
I A.directory /rst is_usually arranged in columns, typically 24
characters wide. Each list'elemLnt should have a'maximum of 20
characters.

. . .!iro
typically start with a !ist open,tag, followed by the list items
(delimited by a list item tag), then a list c-iose tag. The
ListDemo package
Chapter l9: Oracle's WebServer Program 7 47

illustrates different types of lists. The package is below, and Figure 19-4
shows the output of an ordered list in Microsoft lnternet Explorer 3.0.
-- AvaLla.ble online as liatdemo.eql
CREATE OR REPI,ACE PACKAGE I,iSTDEMO AS
- PROCEDUF.E Go;
PROCEDURE Showlist (p-ListT\pe IN CIIAR) ;
EII\ID LiStDemo;

CREATE OR REPI.ACE PACKAGE BODY LiSIDemo AS


-- Set c_OWAPath to the virtual path (including the DCD) where
-- the oracle Web Agent is installed.
c-OWAPath CONSTAIIT VARCHAR2 (50) :=' /example/owa/' ;

-- Presents the user with the available list t1pes.


PROCEIXTRE thorrchoices IS
c_ListPath VARCIaR2 (100) :=
c-OWAPATH | |'ListDemo.Showlist?p List\pe=' ;
BEGIN
HlP. Iine,
HTP.p('C1j-ck on any of the following links to see the');
HTP.p('students in the specified list t14pe.' | | nff.para);

HTP.anchor(c ListPath 'U' , 'Unordered') ;


HTP.anchor(c ListPath 'O' , 'Ordered');
HTP. anchor ( c_ListPath 'M' , 'Menu') ;
HTP . anchor ( c-ListPath 'D' , 'Directory');
END ShowChoices;

-- Presents the first and last names of the students in the


-- specified list t1pe.
PROCEDT,RE Showl,I.st (D-I,iEET?De IIiI CHAR) IS
v_Tirle vARcrrAR2 (20) ;
v_ListOpen vARClaR2 (10 ) ;
v_ListClose VARCIIAR2 (10) ;
CURSOR c Names IS
SELECT first-name | | | | last-name name
FROM students
ORDER BY last_name;
BEGTN
IF p-ListT\pe = 'U' THEN
v-Title := 'Unordered List','
v-ListOpen : = HTF.ulistOpen;
v_ListClose := HTF.ulistClose;
ELSIF p-ListT\pe = 'O' THEN
'v-Tit1e := 'Ordered List';
7 48 OracleS PUSQL Programming

v_Listopen : = HTF. o1j_stOpen;


v_ListClose := HTF.olistClose;
ELSfF p_ListTlpe = ,M, THEN
v_Titl-e : = ,Menu List, ,.

v_ListOpen : = HTF..menulistOpen,.
v_ListC]ose : = HTF.menulistCl_ose;
ELSIF p_ListT\@e = ,D' THEN
v_Titl-e := ,Directory List,,.
v_ListOpen : = HTF. dirlistOpen;
v_ListCl-ose : = HTF. di_rlistClose,.
END IF;
HTP.htmlOpen;
HTP. headopen,.
HTP . title (v_Title ) ;
HTP. headclose;

HTP. bodyOpen;
-- Output the list itself.
HTP . p (v_ListOpen) ;
FOR v_NamesRec IN c_Names LOOP
HTP. listltem (v_NamesRec . name ),.
END LOOP;
(v-Listclose ) ;
Tt"'n
-- Output the choices.
ShowChoices,-

OWA_UTfL . signature ( 'ListDemo . Showlist , ) ;


HTP. bodyClose;
END Showlist;
PROCEDT'RE Go rg
BEGIN
HTP. htmlOpen,.
HTP. headOpen;
HTP. title ( ,List Demo, ) ;
HTP . headclose t

HTP. bodyOpen;
HTP.header(2, ,Welcome to the list demo,);
ShowChoi-ces,.
OWA_UTIL. signature (, ListDemo. Go' ) ;
HTP. bodyClose;
HTP. htmlClose t
END Go;
END ListDemo;
Chapter l9: Oracle's WebServer Program 7 49

FIGURE l9-4. Output of ListDemo in lnternet Explorer

Character Format
The character format tags specify how characters should be formatted. They
do not specify exact characteristics, however. Rather, each browser is free
to render the style how it chooses. For example, the STRONG tag is usually
rendered as bold text, but some browsers could choose to have the text
blink. Specific formattinB tags are described in the next section, "Physical
Format." The following example illustrates some character format tags. The
.lg-5.
output from CharDemo in NCSA Mosaic 3.0 is shown in Figure

CREATE OR REPLACE PROCEDURE CharDemo AS


BEGIN
HTP . htmlOpen;
HTP . headOpen;
HTP. tj-tle ('Character Formatting tags') ;
HTP. headClose;

HTP . bodyOpen;
HTP.header(2,'This page demonstrates character formatting
tags. ') ;
750 OracleS PUSQL programming

HTP.para;
HTP.cite(,This text is a citation, usually rendered as
italics. ,) ;
HTP.parat
HTP.code(,This text is code, usually rendered as
a monospace
font. ' ) ;
HTP. para;
HTP.stronq('This text is strong., usually rendered as
HTP. para;
bold.,);

OWA_UTIL. signature ( 'CharDemo, ) ;


HTP. bodyclose;
HTP. htm1C1ose,.
milD CharDemo,.

Physical Format
The physical format tags specify the physical attributes
of the text. There are
three subprograms in this category: riolo, rrAlrc, and
TELETYPE. BOLD

FIGURE l9-5. OutputofCharDemo in NCSA Mosaic


Chapter l9: Oracle's WebServer Program 75 I

specifies that its argument should be rendered in bold, ITALIC specifies


italics, and TELETYPE specifies a typewriter font, such as Courier.

Form
The form tags are used to implement HTML forms. A form can accept input
from the user, in the form of CUI elements such as text entry fields, and
check boxes. This input is passed to the seryer using the POST method. The
Web agent automatically detects this protocol and passes the entered data
to a PVSQL procedure. As an example, consider the FormDemo package:
-- Aveilab].e oal-Lue as fomdoo.sql
CREATE OR REPLACE PACKAGE FoTmDemo AS
- PROCEDTIRE GO;
pRocEDuRE process(p_Checkbox rN VARCHAR2 DEFAULT ,off,,
p_Password IN VARCIIAR2 DEFAULT NttLL,
p-Radio rN DEFAULT NULL);
EtiID FormDemo, 'ARCHAR2

CREATE OR REPLACE PACKAGE BODY FonnDemo AS


-- Set c_OWAPath bo the virtual path (including the DqD) where
-- the Oracle Web Agent is installed.
c-OWAPath CONSTANT VARCHAR2 (50) :=' /example/owa/, ;

PROCEDURE SboICForn IS
BEGIN
HTP. li.ne;
-- First open the form, specifying the process procedure as the
-- target URL.
HTP.formOpen(curl => c_OWAPath I I'FormDemo.Process, ),.

-- Now print text, followed by input devices.


some
HTP.p('Welcome to a HTML form. FilI out some information,):
mP.p('below, and press the , ,Suhnit,,button to process it., );
HTP.p('Press ''Reset'' to clear your entries., ),.
HTP.para;

HTP.p('Here is a'bheckbox: ');


HTP. formCheckbox(cname => 'p_CheckBox, );
HTP.para;
HTP.p('Here is a password entry: ,);
HTP . f ormPassword ( cname, =>
-g_FS6word,,
752 OracleS PUSQL Programming

csize => 10);


HTP.para;
HTP.p('Sel-ect one of the following radio buttons:, );
HTP. n1;
HTP. formRadio(cname =>,p_Radio,, cval-ue -> 'One') ;
HTP.p('One') ;
HTP. formRadio (cname => .p_Radio,, cvalue -> 'Tb/o') ;
HTP.p('rwo';;
HTP.formRadio(cname =>,p_F.adio,, cvalue ->'Three,);
HTP.p('Three' | | urr"n1);
HTP. fonnRadio(cname => .p_Radio,, cva]ue => 'Four' ) ;
HTP.p('Four') ;
HTP. forrnRadio (cname => ,p_Radio, , cvalue => 'Five');
HTP.p('Five' | | Hrr.para) ;
HTP. formsubmit;
HTP. f orfirReset;
HTP. formClose;
END ShowForm;

PROCEDURE Process(p_Chechbox IN VARCHAR2 DEFAI'IJT,off,,


D_paggword fDMRCIIIR2 DEFAI'IJT NUIJtrJ,
p_Radio IN VARCIIAR2 DEFAI'IJT NT'I,D) Is
BEGIN
HTP. htmlOpen;
HTP. headopen;
HTP. title ( 'Form Results, ) ;
HTP. headClose;

HTP. bodyopen;
HTP.header(2,'Form Resul_ts:, ) ;
HrP.p('p_Checkbox = , ll p_checkbox ll urr.nt;;
HTP.p('p_Password = , ll p_password ll HTF.n1);
HrP.p('p_nadio =, ll p_Radio ll Hrr.para);
ShowForm;
OWA_UTTL. signature ( ,FormDemo. process ,
) ,.

HTP. bodyClose;
HTP . htmlClose;
END Process;

PROCEDT'RE G'o Ig
BEGIN
HTP. htmlOpen;
HTP . headOpen;
HTP. title ( 'Forms Demo, ) ;
HTP. headClose;
Chapter l9: Oracle's WebSerryer Program 753

HTP. bodyOpen;
ShowForm;
OWA-UTIL. signature (' FormDemo. Go' ) ;
HTP. bodyClose;
HTP . htmlClose;
END Go;
END FornDemo;

The main procedure for setting up a form is HTP.FORMOPEN. The syntax


.19-6
is below, and the parameters are described in the Table 19-4. Figure
shows the output of FormDemo.60 in lnternet Explorer.

PROCEDURE FORMOPEN(curl lN VARCHAR2,


cmethod I N VARCHAR2 DEFAULT'POST',
ctarget lN VARCHAR2 DEFAULT NULL,
cenctype lN VARCHAR2 DEFAULT NULL,
cattributes lN VARCHAR2 DEFAULT NULL);

Parameter DatatyPe DescriPtion


curl VARCHAR2 URL which will
process the form. This
is typically another PVSQL stored
procedure, called through the Web
Agent.
cmethod VARCHAR2 HTTP method to use---either'GET' or
,POST'.

ctarget VARCHAR2 TARCET attribute-if set, the curl will


be opened in a new window.
cenctype VARCHAR2 Encoding type to use when sending the
data. lf not specified, the cmethod will
usually dictate the protocol.
cattributes VARCHAR2 Any additional attributes desired.

TABLE l9-4. Parameters for FORMOPEN


754 OracteS pUSeL programming

FIGURE l9-6. Outputof FormDemo.60 in tnternet Explorer

FORMOPEN will generate HTML code similar to

<FORM ACTION=,,curl,' METHO D=,,cmethod,, TARGET=,,c


target,,
E N CTYPE=,,c e nctype,' cattr i butes>
Chapter l9: Oracle's WebSerryer program
255

TIP
It is a good idea to have defaults for all of
the parameters accepted by an action
procedure. lf the user does not enter a value
for a form entry, then that parameter will not
be passed. lf there is no default, then an
error will be raised and'the procedure call
will not be successful.

Table
The table formatting subprograms are used to create HTML tables. Tables
consist of rows and columns, each of which can contain any formatted
HTML. The TableDemo procedure demonstrates the use of iome of the
table formatting procedures. The listing follows, and Figure 19-Z shows the
output in Netscape 4.O.

-- Available online ee tabldoo.sql


CREATE OR REPLACE PRoCEDURE TableDemo AS
- CURSOR c_Students rS
SELECT *
FROM students
ORDER BY ID,.
BEGIN
HTP.htmlopen;
HTP. headopen,.
HTp. title ( ,Tab1e Demo' ) ;
HTP. headclose;

HTP. bodyOpen;
HTP.tableopen(cborder =>,BORDER=1, );

-- Loop over the students, and for each one output two table
-- illl:-ll1:l-Tl ::i::11:l "n''
I first_name last_name I

ID +---------+-----------------+
I major I current_credits I

-- +----+---------+- ------+
FOR v_StudentRec in c_students LOOP
HTP. tableRowopen;
HTP. tableData (crowspan => 2,
cvalue => HTF.bold(v_StudentRec. ID) );
HTP. tableData (ccolspan => 2,
756 OracleS PUSQL programming

calign ,CENTER'
,

cvalue v_StudentRec.first_name ll
I
v_Student.Rec . last_name ),.
HTP. tab1eRowClose;
HTP. tableRowopen;
HTP. tableData ( cvalue 'Major: , ll v_studentRec.najor)
HTp . tab.l:eData ( cvalue 'Credits: , I I

v_StudentRec . current_credi ts ) ;
HTP. tableRowCIose;
END LOOP;

HTP. tableclose;
OWA_UTIL. signature ( , TableDemo , ) ;
HTP. bodyClose;
HTP. htmlClose;
END TableDemo,.

FIGURE I9-7. Output ofTableDemo in Netscape 4.0


Chapter l9: Oracle's WebServer Program 757

OWA_UTIL
The OWA_UTIL package adds additional functionality. lt is wriften using
the HTP and HTF packages. There are three different sets of subprograms
in OWA-UTIL, which are described in the following sections:

I HTML Utilities
I Dynamic SQL Utilities
I Date Utilities

TIP
ln order to use the OWA-|JT|L package, the
Protect-OWA-Pkg parameter must be set to
FALSE in the OWA configuration for the
Web Request Broker. For more information,
see the online documentation.

HTML Utilities
The following table lists the subprograms available in this cateSory. The
functionality ranges from outputting a signature to displaying an Oracle
table as an HTML table. These subprograms are described in Table 19-5,

Subprogram Description
signature P';l!;:,lT:r;ffii,::;'#'fy3xjJ""Jffiaru:n
on August 9, 1995 09:30." A link to show the PL/SQL
source can also be included.
showsource Outputs the source of the supplied package or
procedure. lf a packaged procedure is specified, then
the entire package is output.

TABLE I9-5. OWA UTILHTMLUtilities


758 OracleS PUSQL programming

Subprogram Description
showpage Outputs (using DBMS_OUTPUT)the last page which
was generated using the Web Toolkit. This ii useful to
test the output of procedures in Sel*plus or
SQl-Station. For more information, see the section
"Development Environments for OWA procedures,' at
the end of this chapter.
get_cgi_env Returns the value of the specified CCI environment
variable. lf this variable is not set, then this function
returns NULL.
print_cgi_env Outputs the values of all the CCI environment
variables. This is useful for debugging.
mime header Used to change the default MIME header which the
Agent returns. This procedure must be called before
any HTP.PRINT or HTP.PRN calls; otherwise, the
Web Agent will return the default header.
redirect url Specifies that WebServer visit a different URL. As the
generated code is part of the header, this must be
called before any HTP:PRINT or HTp.pRN calls.
status line Sends a standard HTTP status code to the client. As
the generated code is part of the header, this must be
called before any HTP.PR|NT or HTp.pRN calls.
header close Explicitly closes the header. This must be called
before any HTP.PRINT or HTp.pRN calls if
MIME_HEADER, REDI RECT_U RL, or STATUS_LI NE
has not been called with the bclose_header parameter
set to TRUE.
get_owa_ Returns the full service path (including the DCD) for
service_path the currently executing procedure.
tableprint Prints.the contents of a database table as either an
HTML table or preformatted text.
who_called_me Returns information about the pLlSeL object which is
executing,.including the name and type of object.

TABLE l9-5. OWA UTIL HTML Lltitities(Continued)


Chapter l9: Oracle's WebSeryer program
lSg

and the UtilDemo procedure demonstrates several of them. For more


information on each subprogram, see the web server online documentation.

-- Availa.ble online as utildeno.sql


CREATE OR REPLACE PRoCEDURE UtilDemo AS
v_Temp BOOLEAN;
BEGIN
HTp. htmlOpen;
HTP. headOpen;
HTP. Iitle ( ,oWA-UTIL HTML Demos , ) ;
HTp. headclose,.

HTP . bodyOpen;
HTP.p(',This page demonstrates several 0f the HTML ut.ilities ,);
HTP.p('in rhe oWA_UTrL packagre.' | | Hrr.para);
HTP.header(2,,print_cgi_env, ) ;
HTP.p('The foll_owing is generated by , );
HTP . bold ( ' OWA_UTIL. print_cgi_env, ) ;
Hrp.p(,:, ll Hrr.nry;
OWA_UTIL . print_c gi_env ;

HTP. header (2 , 'get_owa servicejath' ) ;


HTP.p('The following is g,enerated by , );
HTP. bold ( ,OWA_UTIL, get_owa_service3ath, ) ;
HTP.p(,r, | | Hrr.nry;
HTP . p ( OWA_UTIL. get_owa_service3ath) ;

HTP.header(2,,tableprint, );
HTP.p('The following table (containing information from ,)
,..
HTP.p('registered_students) is generated by, ) ;
HTP. bold ( ' OWA_UTrL . tableprint , ) ;
HrP.p(':' llHrn.nry;
v_Temp := OWA_UTIL.tableprint(
ctable =>,registered_students,,
cattributes =>,BORDER=1.,
ntable_tlpe => OWA_UTTL. html_table,
cclauses => ,ORDER By student_id, ) ;
HTP.p('Here is the same data, as a preformatted table instead:,),.
HTP. n1;
v_Temp := OWA_UTfL.tableprint (
ctable =>,registered_students,,
cattributes =>'BORDER=1,
7 50 OracleS PUSQL programming

ntable_type => OWA_UTIL. pre_table,


cclauses => ,ORDER By student_id, );
OWA_UTIL. signature ( ,UtiJ_Demo, ) ;
HTP. bodyClose;
END Uti]Demo;

Dynamic SQL Utilities


These subprograms allow you to generate HTML tables and lists from
a
sQL query. There are three overloaded subprograms which implement this
functionality: BIND-VAR|ABt-ES, cELLSpRtNT, and LtsrpRtNT. Brief
descriptions of each subprogram follow, with an example afterwards.

TIP
Because Bl N D_VARIABLES uses
DBMS_SQL for its processing, do not
include a semicolon in the string that
is input.

CELLSPRINT This procedure wilr format the results of the query into
an HTML table. lt is overloaded to accept either a query as a string,'or a
cursorprepared with BIND_VAR|ABLES. lt can also scroll through"ihe result
set of the query, by returning selected rows only.

LlsrPRlNT LlsrpRlNT will format rhe query results into an HTML


popup selection list (or picklist), designed to be'used in a form. ln order
[o use LlsrPRlNT, the query must select three corumns, which have the
following meanings:

I Column 1: Value to be returned by the picklist.


I Column 2: String to be displayed in rhe picklist.
I Column 3: lf non-NULL, this item will be preselected.
Chapter l9: Oracle's WebSerrrer Program 7 5l

The DynamicDemo package demonstrates how to use these utilities.


Execute DynamicDemo.Go to start the demo.

-- Available online as dlmdemo.sql


CREATE OR REPLACE PACKAGE D]MAMiCDEMO AS
PROCEDURE Go;
PROCEDURE PToceSs(p-QueTy IN VARCHAR2 DEFAULT NULL,
p_Tlpe IN VARCHAR2);
PROCEDURE ListProcess (p-Value IN VARCHAR2 DEFAULT IIULL) ;
END DlmamicDemo;

CREATE OR REPLACE PA,CKAGE BODY DynamicDemo AS


-- Set c-OWAPath to the virtual path (includj-ng the DCD) where
-- the Oracle Web Agent is installed.
c_OWAPath CONSTANT VARCHAR2 (50) :=' /exampTe/owa/' ;

PROCEDITRE ShonForilr IS
BEGIN
HTP. line;
HTP.p('Enter a query in the box be1ow, and select the ');
HTP.p('output t]4)e, then cl-ick "Submit". If you choose ');
HTP.p('the pickfist, then your query shoufd be of the ');
HTP.p('following form:' \ ;
HTP. n1;
HTP.p('Column 1: Resul-t returned for this item' | | Hrr.nf);
HTP.p('Column 2: String displayed for this item' l] Hrr.nfl;
HTP.p('Column 3: NULL or non-NULL. If non-NULL, the ');
HTP.p('current field will be flagged as selected.');
HTP . para;
-- Fi-rst open the form, specifying the Process procedure as .the
-- target URL.
HTP. f ormopen (curf => c-OWAPath | | 'Dlmarnj-cDemo. Process' ) ;
HTP. formTextArea (cname => 'p_Query' ,
nrows => 5,
nccrlumns => 40) ;
HTP. n1;
HTP.p('Output tlpe:' I ;
HTP.p('HTML Table') ;
Hrp. rormRaur" ,:1il:.=1,,y;Ifi!, ,

cchecked => 'CHECKED') ;


HTP.p('Picklist');
HTP. fornRadio(cname =>'P-TYPe',
cvalue => 'list' ) ;
7 62 OracleS PUSQL Programming

HTP. formSubmlt;
END ShowForm;

/* Main entry procedure. */


PROCEDI,RE Go TS
BEGIN
HTP . htmlOpen;
HTP. headOpen;
HTp.title(,OWA_UT]L Dynamic SeL Utilities Demo, ) ;
HTP. h-eadClose;

HTP. bodyOpen;
ShowForm;
OWA_UT] L. s j-gnature ( ,ftrmamicDemo. Go,
);
HTP . bodyClose;
HTP. htmlCl_ose;
END Go;

/" Processes the main form. */


PROCEDTRE ProceEs(p_euery fN VaRCHAR2 DEFAITIJT NULIJ,
p_.,pype rN VARCIIAR2 ) IS
v_CursorID INTEGER;
BEGTN
HTP. hLmlOpen;
HTP.headOpen;
HTP. title ( , euery Results, );
HTP . headClose;

HTP. bodyOpen;
v_CursorID : = OWA_UTfL . bind_variabfes (p_euery) ;
HTP.p('Output from query ,);
HTP.bold(p_Query) ;
Hrp.p(,:, I I urr.para);
IF p_T\@e = ,tabfe, THEN
HTP.tableOpen(cborder =>,BQRDER=1, ) ;
OWA_UTTL. cellsprint (v_CursorlD ) ;
HTP. tableClose;
ELSE
HTP.formOpen(curl => c_OWAPath | 'Ulmamicoemo. Listprocess , );
I

OWA_UTIL . 1 i s tprint v_Cursorf D, 'p_Va1ue' , !0);


(

HTP. formsubmit;
HTP. f ormcl_ose;
END IF;
ShowForm;
Chapter l9: Oracle's WebSeryer Program 7 63

OWA_UTfL. si-gnature ( 'DlmanicDemo. process, ) ;


HTP. bodyClose;
HTP. htmlClose;
END Process;

/* Processes the form containing the picklisL. */


PROCEDT'REListProcess(p-Va1ue IN VARCIIAR2 DEFAT'LT I{I'LL) Is
BEGIN
HTP. htmlOpen;
HTP . headOpen;
HTP. title ( ,List Results, );
HTP. headCfose;

HTP. bodyOpen;
HrP.p('you picked' | | p_value I l, .' I ;
ShowForm;
OWA_UTIL. signature (, DynamicDemo. Listprocess, ) ;
HTP. bodyClose;
HTP. htmlcfose;
END ListProcess;
END [mamicDemo;

Date Utilities
The date utilities in oWA-UTIL implemenr two different features to help
you manage date values in Web pages. The CALENDARPRINT procedure
will output a calendar (as an HTML table) based on the results of a query,
while the cHoosE-DATE procedure will generate code to accept a date as
a day, month, and year.

CALENDARPRINT CALENDARPRINT takes as input either a string


containinS a query, or a cursor prepared with
OWA_UTIL.BIND_VARIABLES, and generates a calendar based on the
results of the query. The query can have either two or three columns:

I Column 1: DATE value used to correlate the returned data with the
displayed calendar.
I Column 2: Text, if any, to be printed for this date.
I Column 3: lf this column is present, then the second column will
be displayed as a link, with destination indicated by column 3. lf
there are only two columns, then the values will be displayed as
text only.
754 OracleS PUSQL Programming

cHoosE-DATE cHoosE DATE will output three fields, to be used


to input the year, month, and day. The target of these fields should be a
procedure with parameter rype owA_uTlL.DATETYPE. owA ulL.To
DATE can convert DATETYPE values to a standard Oracle DATE.
The DateDemo package demonstrates the use of these utilities, and
Figure 19-B shows the output of DateDemo.Go in lnternet Explorer. ln
order to create the package, the calendar table is first created with:

-- Available online aa part of datedeno.sql


CREATE TABLE calendar (
today DATE,
descrj-ption VARCHAR2 (25),
link VARCHAR2 (20 )

INSERT INTO calendar (today, description, link)


VALUES (SYSDATE - 5, 'Hel1ot ,, '/ /www.orac]e.com,);
INSERT INTO cal_endar (today, description, link)
VAIUES (SYSDATE - 4, 'Getting there... ,, ,/ /www.oracle.com, );
INSERT INTO calendar (today, description, l_ink)
VALUES (SYSDATE - 3, s it going?,, ,/ /www.orac1e.com,);
INSERT fNTO calendar (today, description. l_ink)
VALUES (SYSDATE - 2, ,Soon...,, '//vw,tw.orac1e.com,);
INSERT INTO calendar (today, descriptj-on, link)
VALUES (SYSDATE - 1, 'Tomorrow, tomorrow,, ,//www.oracle.com,);
TNSERT INTO cal_endar (today, description, link)
VALUES (SYSDATE, 'Made i_tt , , , / /v,tww.oracle.com,) ;
INSERT fNTO ca1endar (today, description, link)
VALUES (SYSDATE + 1, 'Did you miss it?,, ,/ /wtrtw.oracle.com,);
TNSERT INTO calendar (today, description, l-ink)
VALUES (SYSDATE + 2, 'Sure hope not...,, ,/ /www.orac1e.com,);
INSERT INTO cal_endar (today, description, link)
VALUES (SYSDATE + 3, 'It was pretty cool_,, '/ /www.oracl-e.com, );
INSERT INTO calendar (today, description, link)
VALUES (SYSDATE + 4, 'Better l_uck next year,,
' / /www. oracle. com' ) ;
INSERT fNTO calendar (today, description, link)
VALUES (SYSDATE + 5, 'Goodbye!', '/ /www.oracfe.com,) ;
Chapter l9: Oracle's WebServer Program 7 65

FIGURE'I 9-8. Output of DateDemo in lnternet Explorer

The package itself is defined below.

-- Available online as datedemo.sql


CREATE OR REPLACE PACKAGE DateDemo AS
PROCEDURE Go,.
PROCEDURE Process (p_DateVal IN OWA_UTIL.dateTlpe) ;
END DateDemo;

CREATE OR REPLACE PACKAGE BODY DateDemo AS


-- Set c_OWAPath to the virtual path (including the DCD)
-- where the Oracl-e Web Agent is instal-l-ed.
c_OWAPath CONSTANT VARCHAR2 (50) :=' /example/owa/' ;

-- Receives the date selected.


(p-Dateval IN OWA-IITIL.daIeTIPe) IS
PROCEDI,RE ProceEE
7 66 OracleS PUSQL Programming

BEGIN
HTP. htmlopen,
HTP. headOpen;
HTP. title ( 'ResuIts' ) ;
HTP. headClose;
HTP. bodyOpen;
HTP.p('You picked ');
HTP . p (TO_CIIAR (OWA_UTIL. todate (p_Dateval ))) ,-

HTP. n1;
OWA_UTIL . signature (' DateDemo . Process ' ) ;
HTP . bodyClose;
HTP . htmlClose;
END Process;

-- Main entry procedure.


PROCEDT'RE Go IS
BEGIN
HTP. htmlopen;
HTP.headOpen;
HTP. title ( 'OWA-UTrL Date Utilities' ) ;
HTP. headClose;

HTP. bodyopen;
HTP.p('Welcome to the Date demo. Here"s a calendar, '\;
HTP.p('created with' | | urr.boi-d('OWA_UTfL.calendarprint, ));
HrP.p(':' ll srr.nr);
OWA_UTIL calendarprj-nt (
.
'SELECT * FROM calendar OR-DER BY today');
HTP.para;
HTP.p('Enter a year, month, and day bel-ow, and then press');
HTP.p(' submit:' | | Hrr.nfl ;
HTP.formOpen(curl => c_OWAPath I I 'DateDemo.Process');
OWA_UTIL.choose_date(p_narne =>'p_DateVal' ) ;
HTP. formSubmit;
HTP. formClose;
OWA_UTfL. signature ( 'DateDemo. Go' ) ;
HTP. bodyClose;
HTP. htmlClose;
END Go;
END DateDemoi
Chapter l9: Oracle's WebSeryer Program 7 67

OWA_IMAGE
The OWA_IMACE package provides a way to handle image maps. An
image mapis a graphic on a'web page. Any graphic can be used as an
anchor for a link, but an image map is sensitive to the location within the
graphic where you click. Based on the x and y coordinates within the
graphic, the web server can return different URLs. Unlike other web
servers, which process image maps by using a separate CCI program, the
Oracle web server can handle them internally. A procedure that references
the OWA_IMACE package is used as the target of an image map to handle
the x and y coordinates. OWA_IMACE defines one datatype, POINT,
which contains the x and y values. Two functions are provided to retrieve
these values, defined with

FUNCTION CET_X(p lN POINT) RETURN INTECER;


FUNCTION CET_Y(p lN POINT) RETURN INTECER;

where p is a point selected with an image map. The lmagemap package


illustrates one way of using OWA IMACE:

-- available online as irnagemap.eql


CREATE OR REPLACE PACKAGE Imagemap AS
PROCEDURE Go;
PROCEDURE Process (p_ImS IN OWA_IMAGE.POINT) ;
END Imagemap;

CREATE OR REPLACE PACKAGE BODY Imagemap AS


-- Set c_fmaqePath to the virtual path on your server where the
-- boxes.gj-f file has been stored.
c_ImagePath CONSTANT VARCHAR2 (50) : = '/ows-img/boxes .gtf' ;

-- Set c OWAPath to the virtual path (including the DCD) where


-- the Oracle Web Agent is install-ed.
c-OWAPath CONSTANT VARCHAR2 (50) :=' /exampLe/owa/' ;

PROCEDURE ShowBoxes IS
BEGIN
HTP.p('C1ick anlrwhere on the lmag'e below. | | Htr.para);
768 OracleS PUSQL Programming

--Set up a form with one clickable image. The target of this


-- image j-s
the Process procedure. Since the type of this form
--field is an image, the p_Img parameter will be passed a
--value of tlpe OWA_IMAGE.POINT.
HTP.formopen(curl => c_OWAPath I l,Imagemap.process, ) ;
HTP. formlmage (cname =>'p_Imq',
csrc => c_ImagePath,
cattributes => 'BORDER=0' ) ;
HTP. formClose;
END ShowBoxes;

PROCEDURE Go IS
BEGIN
HTP. htmlOpen;
HTP. headOpen;
HTP:title ( 'Tmagemap test' ) ;
HTP. headclose;

HTP. bodyOpen,-
ShowBoxes;
HTP. bodyCJ-ose,.
HTP. htmlClose;
END Go;

PROCEDURE Process(p_ImS IN OWA_IMAGE.POINT) IS


v_x INTEGER := OWA_IMAGE.GET_X(p_Img) ;
v1r INTEGER := OWA_IMAGE.GET_Y(p_Imq) ;
v_Color VARCIIAR2 (5 ) ;
BEGIN
fF v_x < 200 THEN
fF vJ < 100 THEN
v_Color := 'Blue';
ELSE
v_Col-or := 'Red';
END IF;
ELSE
IF vJ < 100 THEN
v_Color := 'Green';
ELSE
v-Color := 'Black';
END IF;
END IF;
Chapter l9: Oracle's WebSeryer Program 7 59

HTP. htmlOpent
HTP. headOpen;
HTP. title ( 'Imagemap resul-ts' );
HTP . headcl-ose;

HTP. bodyopen;
HTP.print('Processed. x='ll,r_* ll 'andy=, llt__v) ;
HTP.print('. This means that you selected ,the , | | v_Co1or);
HTP.print(' box., | | Hrr.para) ;
ShowBoxes;
HTP. bodyclose;
HTP . htmlclose;
END Process;

END Imagemap;

NOTE
ln order to set this up on your system, copy
the boxes.gif file (found on the CD) to an
image directory on your web server and
specify the path in c_lmagePath.

ln order to set up the image map, you create a form with one clickable
image. The target of this image is lmageMap.Process. The form is created
by the ShowBoxes procedure. The boxes.gif image is simply four boxes of
different colors. The results of calling lmageMap.Go and clicking on the
blue box (in Netscape Navigator 4.0) are shown in Figure 19-9.

OWA COOKIE
OWA_COOKIE allows you to manage HTML cookies. A cookie is a piece
of data stored on the client, rather than the server. Cookies can be used to
maintain state between calls to the server. ln normal processing, the client
is not aware that the cookie exists. However, many browsers do have a
setting that enables the user to be notified of cookie transmissions and,
optionally, to refuse them.
770. OracleS PUSQL Programming

[ic fdt View Ao lomunicrts Eelp

{+:ilfla-#Eifdi#g
B+k .:n,
:rd Fdoad Home Seitch GddE ftir* Securit! I rii

Processed X = I 16 md Y = 66 This meMs that vou selecied the Blue bor

Click eJMhere on the ifrage below

FIGURE l9-9. Resu/ts of clicking on an image map

Datatypes
The HTTP specification for cookies stipulates that a cookie is a name-value
pair. The value can be set or retrieved for a given name. The name and
value are limited to 4,096 bytes, and multiple values can be stored with
one name. To implernent this, the OWA_COOKIE package defines the
following datatypes:

TYPE VC-ARR IS TABLE OF VARCHAR2(4096)


INDEX BY BINARY INTECER;
TYPE COOKIE IS RECORD (

name VARCHAR2(4096),
vals VC_ARR,
num_vals INTECER);

The OWA-COOKIE.COOKIE datatype is implemented as a record with a


PLISQL table so that it can contain multiple values.
Chapter l9: Oracle's WebSeryer Program TT I

SEND
The SEND procedure is used to pass a cookie to the client for storage. lt is
defined with

PROCEDURE SEND(name lN VARCHAR2,


value lN VARCHAR2,
expires lN DATE DEFAULT NULL,
path lN VARCHAR2 DEFAULT NULL,
domain lN VARCHAR2 DEFAULT NULL,
secure lN VARCHAR2 DEFAULT NULL);

SEND must be called from the HTTP header of a page, as illustrated by the
example at the end of this section. SEND will generate an HTTP header line
that looks like:

t - c o oki e' path=pa th


::: ;:;1" "'
se
;:il:;r:;if i

The only required parameters are name and value.lf the others are not
specified, the appropriate clause is not generaled. expires specifies the time
when the cookie will expire. lt cannot be retrieved after this time. lf it is not
specified, the cookie expires when the client session is ended (that is, when
the browser is closed).

GET
The GET function returns a cookie with the specified narne. lf there is no
data on the client for the requested name, num_vals in the returned cookie
will be zero. lt is defined with

FUNCTION GET(name lN VARCHAR2) RETURN COOKIE;

where name is the name of the desired cookie.

GET ALL
CET-ALL wiil return all the name-value pairs of the cookies retrieved from
the browser, in the order in which they were sent. lt is defined with
772 Oracle8 PUSQL programming

PROCEDU RE GET_ALL( names OUT VC_ARR,


vals OUT VC_ARR,
num vals OUT TNTEGER);

REMOVE
REMOVE forces the expiration of an existing cookie. Like SEND, it
must be
called from the HTTp header. lt is defined r,iith

PROCEDURE REMOVE( name tN VARCHAR2,


val lN VARCHAR2,
path tN VARCHAR2 DEFAULT NULL);

It generates a line similar to

Set-Cookie : name=waLue path=path expires=01_,JAN_1990


the path parameter isn't specified, then the path clause is omitted.
lf
Expiration is caused by setting the date to one that has already passed
fanuary 1, 1 990).

Example
The CookieDemo procedure demonstrates the use of owA cooKlE.
-- Available onlLne ae cookie.sql
CREATE oR REPLACE :FjiOcnounn cookieDemo (
p_NewVal rN NUIBER DEFAULT NULL) AS
-- Set c_OwApath to the virtual path (including the DCD) where
-- the Oracle Web Agent is installed.
c_OWAPath CONSTANT VARCHAR2 (50) :=, /example/owa/,
;

v_Cookie OWA_COOKIE. COOKIE ;


BEGIN
-- Retrieve the current var-ue of the cooki_e, and send the new
-- value based on the parameters.
v_Cookie := OWA_COOKIE.cET(,Count, ) ;
OWA-UTIL. MIME_HEADER ( , TexI/htm]. , , FALSE ) ;
IF p-NewVal Ts NoT NULL THEN
OWA_COOKfE.SEND(,Count,, p_Newval + 1) ;
Chapter l9: Oracle's WebSeruer program
TTI

:_--_-- -,-___i!-.i_:.::._:-:._r-:lS = 0) THEN


- i,,-1_!-'vr\_!. _.:l(1,' _-,-:nt,, 0) ;
:_s r
- ,,.--.__- L'i_= . _:r-\l r .Count , , v_Cookie . vals (1) + 1)
r-\-l Ii: ;

.-,,;-r-_- : _: . 5::P_:r \DER_CLOSE ;

I-l :-^.' -.rs-.


L--tr :-s:ir,ra*-
;-: -r
;-: ;=: i -- rs-.

HIP . t.i!-l\-C!ien;
nr:.lriii:;
HTa ,--l
t i"e-'come to the cookie demo, using the owA_cooKIE
HTP.prin!(,packag,e. / I I ffrr.para) ') ;
;
IF p_Neh\-a1 IS NOT NULL THEN
HTp.print(,The cookie va1ue has been reset to ,);
HTP.bold(p_NewVal);
HTp.prinr(,., | | Hrr.para);
ELSIF .,'_Cookie.num_val-s > 0 THEN
HTP.print (,The cookie value is now ,) ;
HTp.bold(v_Cookie.val_s (1) ) ;
HTp.prinr(,., | | urr.para);
ELSE
HTP.print ( ,The cookie va]ue has been set to zero. ,
END TF; );
HTP. para;
HTP.print (,C1ick ') ;
HTP.anchor(c_Owapath I l,CookieDerno.,,here,
HTP'print(' to increment the va1ue, or enter ) a; new vafue
HTP.print(, the box below and cl-ick Submit to reset it.,); in,);
HTP . para;

HTP.formopen(cur1 => c_OWApath .CookieDemo,);


||
HTP. formText (cname => ,p_NewVal,
,
csize => 10) ;
HTP. f ormSubmi-t;
HTP. f ormcl_ose;

OWA_UTfL. signature ( ,CookieDemo, )


;
HTP. bodycl-ose;
HTP. htmlClose;
END CookieDemo;
774 Oracle8 PUSQL programming

Development Environments
for OVVA Procedures
Developing stored procedures fcir the Oracle web agent is much the same
as for other stored procedures: you have a choice oidevelopment
envi ron ments. However, two envi ron ments-seL*pl us and sel-station,
described in the following sections-have utilities that are useful for these
types of procedures.

OWA_UTIL.SHOWPAGE
The output from a procedure meant to be called through the web agent can
normally be seen only in a web browser. Thus tools ru".h Sel*pius are
less useful. However, the oWA_UTrL.sHowpACE procedure ", will echo
the HTML output from the last procedure using DBMS_ourpUT. The
following SQL*PIus session shows how to ur"lt,
SQIJ> exec BodyDeno
PLlSQL procedure successful_1y completed.

SQL> aet SERI/EROIIIPIII ON


SQIJ> exec O$IA_IXIIIJ. SHOWPAGE
<HTML>
<HEAD>
<TfTLE>Body tags demo</TITLE>
< /HEAD>
<BODY>
this page demonstrates several of the tags
available for formatting the text in the body
of a web page, and for j_nserting additional
tags. <p>
<H2>Links< /H2 >
Here are some finks to check out. These are
generated usingr the HTp.anchor procedure.
<P>
<A HREF=" / /www.oracle. com/,'>Oracle Corporation</A>
-D\
<A HREF= " / / www .osborne . com/ oracf e/ index. htm,, >Oracle press</A>
<P>
<A HREF= " / /www.platinum. com/ ,' >pl_atinum Corporation</A>
<P>
<H2>Formats< /H2>
Chapter l9: Oracle's WebServer Program 77 5

<CENTER>These flnes are centered, usrng


<BR>
the HTF.centeropen and HTF.centerClose functions.
< / CENTER>
<STRIKE>Here's some strikethrouqh text.</STRIKE>
< /BODY>
< /HTML>

PLlSQL procedure successfully completed.

SHOWPACE enables you to see the HTML output from a procedure to


verify its accuracy.

SQl-Station Coder
You can also execute OWA procedures from SQL-Station Coder. The
procedure execution window has three execute buttons. The first will
execute the procedure normally. The next two buttons, however, are used
to call a procedure with the Web Agent, as shown in Figure 19-10. The
"Web execute" button will call a browser on the client and pass it the

ql *l iffi ''i-rLr.-FF-..Fb
ql*lsl !!!lplsl Fl xl.+l \l r lElcl El_rl El
|['.r*, lr-
Q Peckase jl
Qfl Frocedure
F 6 sunrorhln ll
!i
I
'el-"-,"] )
Q! rerLo 1e_rRttTrNG tN ,

; 'Crnro
, 3 fuF"t","n.*.
, 3 GFeterenced Er
, S Prvrleges
E Q! clt't c 6.tsc nt vr+Rrr+t
F Ql PFTNTDEN0 tl
:l
!
Execute Frocedure llvouqh 0rele's \tJeh Serier mm1 D0001

FIGURE I 9- ! 0. Web exectfting stored procedures in SQ[-Statio n Coder


776 OracleS PUSQL Programming

correct URL to execute the procedure with the supplied parameters, using
the CET method. The "Cenerate URL" button will allow you to see (and
optionally edi0 the generated URL and copy it to the clipboard. By using
SQl-Station Coder in this manner, you can create web-enabled procedures
the same way you create normal procedures. For more information on
SQl-Station Coder, see Chapter 13 and the SQl-Station online
documentation.

Summary
ln this chapter, we have discussed Oracle's WebServer program and
examined how PL/SQL is an integral part of it. Through the PLlSeL web
agent, a PL/SQL stored procedure can generate HTML output. This allows
you to create dynamic web pages, based on data in the database. We have
discussed the packages in the web toolkit that aid in this, including the
OWA_UTIL package. The chapter concluded with a discussion of
development environments useful for web agent procedures.
780 OracleS PUSQL programming

L/SQL is a very powerful language with many features, but


there are some things for which it is not suited, such as
interaction with the file system and other system devices, or
complex numerical calculations. ln these cases, another
3cL such as C is more appropriate. oracre8 provides the
ability to.call a C procedure directly from pL/Sel as an external procedure,
which allows these additional features to be integrated with the pllsel
environment. ln this chapter, we will discuss how external procedures
work, including the necessary requirements to make them work.

What ls an External Procedure?


An external procedure is a procedure written in a ranguage other
than PL/SQL but callable from a pLlSeL program. fxternil
procedures are a new feature of oracle8. As of oraJle8 version 8.0.3,
the
only supported language for external procedures is c. Future versions of
oracle8, however, will have support for additional languages (such as
Java
or \_++,,.
ur C++). Lonsequenuy/
Consequently, tne
the syntax lor
for declaflng
declaring and calling
calli external
procedures is more generic than it needs to be for the current version, to
accommodate future languages.
How do external procedure calls work? Figure 20-1 illustrates the
communication between a client process and the oracle8 server. The client
process communicates with the shadow process, which in turn
communicates with the rest of the database. The client process sends SeL
statements and PL/sQL blocks to the shadow process, which executes
them
and sends back the results. For more informatlon on client and shadow
processes, including how they interact with other oracle processes, see
Chapter 22.

additional procedures, the shadow process and extproc can communicate


directly, without going rhrough the iitten"r.
Chapter 20: External Procedurer
78 I

Client H Shadow
Process Process

FIGURE 20- 1. Client and shadow processes

There are several things to note about this environment, which illustrate
some requirements for using external procedures:

I The external procedure must be in a shared library. Extproc will


dynamically load the library when the procedure is called. lf
external procedures are called later in the session'that are in a
different library, extproc will also load that library. Thus, external
procedures are only supported if the underlying operating system
supports shared libraries. For example, on Windows NT the
external procedure would be compiled into a DLL, and on Solaris
the external procedure would be compiled into a shared object (.so).
I Each session that calls an external procedure will have its own
extproc process spawned by the SQL*Net listener.
I The listener and extproc must be on the same machine as the
database. As we will see in the next sections, the syntax for creating
a library data dictionary object has no means for specifying a host
name.
782 OracleS PUSQL programming

FIGURE 2O-2. Sel*Nef listener and extproc processes

I The external procedure can in turn make callbacks to the database


to execute additional sel statements or pllsel blocks. Callbacks
are done using the oracle8 Call rnterface (ocl). They are discussed
in the section "Callbacks to the Database" later in this chapter.

Required Steps
ln order to call an external procedure, you must complete the following
steps:

l. Code the procedure in c and compile it into a shared library.


2. Configure the sQL*Net parameter files and start the ristener.
3. Create a library data dictio'nary object to represent the operating
system library.
4. Create a wrapper procedure in pLlSeL that maps pLlSeL
parameters to C parameters.
Chapter 20: External Procedure.
783

Let's examine these steps in detail.

Code the Procedure


The first step is to write the procedure itself. Suppose we want to create an
operating system file and write a string to it. We can do this with the
OutputString procedure defined here:

/* Availab1e online aE part' of outstr.c */


#include <stdio.h>
/* Outputs the string contained in message to a file specified by
path. The file will be created if it doesn,t exist. */
void Outputstring (path, message)
char *path;
char *message,. {
FII,E . Eile_handle;

/* Open the file for writing. */


fi]e_handle = fopen(path, "w" ) ;
/* Output the string followed by a newline. */
fprintf (file_handle, "8s\n", messagre) ;
,/* Close the file. */
fclose ( file_hand]e) ;
)

Once this file is created, we need to compile it into a.shared library. On


a Solaris Unix system, we can do this with the command

cc -c -o /home/utils/stringlib.so outstr.c
which will create the shared library/home/utils/stringlib.so. The command
for doing this on other operating systems will likely vary-consult your
operating system andlor compiler documentation for more information.
784 OracleS PUSQL Programming

TIP
On Unix systems, the makefile
$ORActE_HOME/rdbms/demo/demo rdbms
.mk contains targets for linking shared
libraries. Ihe extproc_nocallback target wi ll
build a library that doesn't make callbacks
to the database, and the extproc_callback
target will build a library that does make
callbacks. (Callbacks are discussed later in
this chapter, in the section "Callbacks to the
Database.") See this makefile for
i nstructions and examples.

Configure the SQL*Net Listener


The listener needs to be configured only once. once it is set up and
running, the extproc process will be automatically spawned as needed.
Configuring the listener requires two files--listener.ora and tnsnames.ora.
Once these files are created, the listener can be started.
The configuration files will vary depending
your
on for example, the default location is
$ORAC This can be overridden by specifying the
TNS_ADMI N envi ronment variable.

listener.ora This file specifies the parameters for the listener. lt should
look something like the following:

*Awailable online as liEtener.ora


#Sample listener.ora for external procedures.
#Replace <<ORACLE_HOME>> with the $ORACLE_HOME dd_rectory,
#replace <<LISTENER_KEY>> with an fpc key, and
#replace <<EXTPROC_SID>> with an identifier for extproc.

listener =
(ADDRESS_LIST =
(ADDRESS =
(PROTOCOL = ipc)
(KEY = <<LISTENER_KEY>>)
)

)
Chapter 20: External Procedurer
785

sid list fistener =


(SID-LIST =
(SID-DESC =
( = <<EXTPROC_SID>> )
SID_NAME
(ORACLE_HOME = <<ORACLE_HOME>>)
(PROGRA!,I = €xtproc)
)

The key thing to note about this configuration file is the


(PROCRAM=extproc) section in the SID list. This entry signifies that this
connection is to be used to spawn extproc, rather than connect to a
database.

tnsnames.ora This file is used to specify SQL*Net connect strings.


extproc has a special connect stri ng---€xtproc_connection_data-that is
defined in tnsnames.ora similar to the following:

#Available online aE tnsnames.ora


#Sample tnsnames.ora for external procedures.
#Replace <<LISTENER_KEY>> with an IpC key, and
#replace <<EXTPROC_SID>> with an identifier for extproc.
extproc_connection_data =
(DESCRIPTION =
(ADDRESS =
(PROTOCOL = ipc)
(KEY = <<LTSTENER_KEY>>)
)

(CONNECT-DATA =
(SID = <<EXTPROC_SID>>)
)

The SID specified in the CONNECT DATA clause of both tnsnames.ora


and listener.ora must be the same. This can be the database SlD, but it does
not have to be. The ADDRESS clause must be the same for both files as
well.
786 OracleS pUSeL programming

NOTE
A single listener (or set of listeners) can
listen both for external w
database connections.
configuration fi les can
databas riptions. For more
informa parameters for
listener. ora, see fhe NetB
Adm i n i strator's Gu ide.

Starting the Listener


lsnrctl (or possibly lsnrctl
operating system prompt
SQL*Plus. Assuming that
e
preceding e*amplei with VBO3 as th
as the tpc key, the foilowing session shows
Yj9l^l"oroc the startup of the
ilstener:

$ lsnrctl start
LSNRCTL for Solaris: Version g.0.3 .0. O _ Production on I2-AIJG-97
01:03 :55

(c) Copyright. 1997 Oracle Corporation. Al_f rights


Starting,/ oracle /app / or acTe / pr oduct,/ g . 0 . 3 /bin/ tns]snrreserved.
: please wait
TNSLSNR for Solari-s: Version g.0.3 . 0.0 _ production
System parameter file is
/ oracle /app / oracre / product / g . 0 . 3 /network/admin/
1is tener. or a
Loq messages written to
/ oracle /app/oracle /product / g . 0 . 3 /network/
1og/ listener. rog
Listening on : (ADDRESS= ( pRorocol=ipc ) (DEV=9 ) (KEy=v803_extproc
) )

connecting to (ADDRESS= (pRorocol,=ipc) (KEy=v803_extproc)


)
STATUS of the LISTENER

Alias LTSTENER
Version TNSLSNR for Solaris: Veision g.0.3.0.0
- Production
Chapter 20: External Proceduret
TgT

Start Date 1,2-AUG-91 01: O4:00


Uptime 0 days 0 hr. 0 min. 1 sec
Trace Level off
Security OFF
SNMP OFF
Li-stener Parameter File
/ or acle / app / or acle / produc t / B.0.3/ network/ admin,/ I i s tener . ora
Listener Log File
/ oracle/ app/oracle/product./ I . 0 . 3 /network/ 1ogl Iistener. 1og
Services Summary.. .
V803 has 1 service handler(s)
The command completed successfully

For more information on using lsnrctl, see the NetB Administrator,s


Guide.

Create the Library


A library is a data dictionary object that contains information about the
operating system location of the shared library on disk. Since the shared
library is outside the database, PL/SQL needs something in the database to
represent it. Libraries are created using the DDL command CREATE
LIBRARY, which has the following synrax,

CREATE LIBRARY library_name {lS I AS}


' operati n g_system_path' ;

where library_name is the name of the new library, and


operating-system-path is the complete path (including directory) of the
shared library on the file system. For example, we can create the library
stringlib using the following statement:
CREATE LIBRARY strinqlib eS
' /home/utils / stringlib. so , ;

The database does not verify that the operating system shared library
actually exists at this point. However, if it does not, you will receive an
error when trying to call a procedure declared to use the library.
788 OracteS pUSeL programming

.has Athelibrary can be dropped using the DRop LTBRARY


syntax
command, which

DROP Ll BRARY I ibrary_name;

where library_name is the name of the library to be dropped.

Privifeges on Libraries rn order to create a library, you need the


CREATE LIBRARY system privirege, simirar to the priuirugu,
required for
other DDL commands..once yoJ h"u" created uiiar^ryi
ylulan ailow
other users to access it by granting the EXECUTE privilegJ
on it to other
users.

Libraries in the Data Dictionary Like other database


objects,
information about libraries is stored in the data ai.tion"iy.
Litrury
information is stored in the user_libraries, all_libraries,
JnJJr" libraries
tables. The main columns in theie tabres are described
here:

Column Datatype Description


library_name VARCHAR2(3O) Name of the library.
file_spec VARCHAR2(2OOO) Operating system path
of the shared library as
specified in the CREATE
LIBRARY statement.
dynamic VARCHAR2(I ) tf y, the library is
dynamic. lf N, it is not.
With Oracle8 version
8.0, only dynamic
libraries can be created.
status VARCHAR2(Z) VALTD or |NVAL|D,
similar to the status
column in other
dictionary views.

The following query shows the information about stringrib:

SQL> SELECT * FROM user_libraries


2 WHERE library_name = ,STRINGLIB,;
Chapter 20: External Proceduret 789

LIBFARY NAME FILE SPEC D STATUS

STRINGLIB /home/utils / stringlib. so Y VALID

For more information on the data dictionary and other information


stored there, see Appendix D.

Create the Wrapper Procedure


ln order to call an external procedure, you need to create a wrapper
procedure. This procedure serves several purposes: it maps the PL/SQL
parameters to C parameters, it serves as a placeholder so that calling
procedures can determine dependencies, and it tells PL/SQL the name of
the external library. A wrapper procedure consists of a procedure
specification (including the parameters, if any), followed by the EXTERNAL
clause. The syntax is as follows:

CREATE IOR REPLACE] PROCE DU RE procedu re-na me lparameter-l i stl


AS EXTERNAL
LIBRARY library-name
INAME external-namel
ILANC UAC E I anguage-namel
ICALLINC STANDARD IC I PASCAL}]
[WITH CONTEXT]
I PARAM ETE RS ( exferna I r am ete r-l i st)l ;
-pa

Each of the clauses is described in the following sections.

LIBRARY This is the only required clause, and it is used to specify the
library that represents the operating system shared library containing the C
procedure. This library must either be in the current schema, or you must
have EXECUTE privileges on it.

NAME This clause specifies the name of the C procedure. lf it is not


specified, it defaults to the name of the PL/SQL wrapper procedure. Note,
however, that PL/SQL names are stored in all uppercase, so if the C
procedure is lowercase or mixed case, a double-quoted identifier is
nece$ary here.
790 OracteS pUSeL programming

LANGUAGE The LANCUACE the language in


which the external procedure is wr
efaults to C,
which as of version 8.0.3 is the onl
external
procedures.

CALLING STANDARD The cailing standard can be


either c or
PASCAL. This determines the order in r,ini.r' parameters
are placed on the
stack. The default is C, which shourd normally be used
for externar
procedures written in C. with the pASCAL ."iling
standard, the external
procedure is responsible for explicitly popping th"e
stack to retrieve the
parametersf and the parameters themselves in r"u"rse order.
"16
wlrH coNTExr This crause wit add a parameter to the C
procedure, which can be used to get ocr handles
used for ."lting back to
the database. This clause is discus-sed in detail in
the section ,,Callbacks to
the Database,' later in this chapter.

PARAMETERS The pARAMETERS crause specifies how the pllsel


parameters of the wrapper procedure are mapped
into C parameters for the '
external procedure. rf it is not specified, then'the defaurt -
,"pping,
used. This clause is discussed in detail in the next
section, ,,parameter ";
Mappings.,,
The following exampre is a wrapper procedure
for outputstring:
-- Available online aE Dart of outstr.eql
CREATE OR REPLACE PROCEDURE Outputstring(
p_path fN VARCHAR2,
p_Message rN VARCHAR2) AS EXTERNAL

LIBRARY stringlib
NAME "OutputString,,
PARAMETERS (p_Path sTRfNG,
p_Message STRfNG);

tring directly from a pLlSeL


ll create a file called output.txt
ngle line ,'Hello World!', This is
and host session that follows.
SQL> BEGIN
2 Outputstring(, /tmp/output.,txt,,,Hel_lo Wor]dl, ) ;
Chapter 20: External Proceduret 79 I

3 END;
4/
PLlSQL procedure successfully completed.
SQL> exit
Disconnected from OracfeS Server Release 8.0.3 .0.0 - Production
With the Partitioning and Objects options
PLlSQL Release 8.0.3.0. O - Production
$ cat /tmp/output.txt
Heflo World!

Parameter Mappings
One of the main issues when communicating between two languages is
how to convert between datatypes. This is true with external procedures as
well: you need to specify how the PLISQL datatypes used for the
parameters in the wrapper procedure map to the C datatypes used for the
parameters in the external procedure. Besides the datatypes themselves,
there are other issues that need to be resolved:

I ln PLISQL, any variable can be NULL. C has no concept of NULL,


and so an additional variable known as an indicator can be passed
to indicate whether the parameter is NULL or not.
I PLISQL includes datatypes that are not in C, and C includes
datatypes that are not in PLISQL. For example, C has no DATE or
BOOLEAN type, while PL/SQL does not distinguish between a one-/
two- or four-byte integer.
I PL/SQL version B can accommodate character variables in different
character sets. These character sets need to be communicated to C.
I C needs to know the current and/or maximum length for character
strings passed from PLISQL.

I PL/SQL needs to know the current and/or maximum length for


character strings passed from C.

The PARAMETERS clause is used to resolve all of these concerns, as we


will see in this section. lt has the following structure:

PA RAM ETE RS ( externa I_par a mete r_l i st) ;


792 Oracle8 PUSQL Programming

The exferna l-parameter_list is a list of parameters, each of which has the


structure
lparameter-name I RETURN) property tBy REFI [external_datatype]
where parameter-nameis the name of the parameter, external_dantypeis
the c datatype to be assigned to this parameter, and property is one or
more of INDICATOR, LENCTH, MAXLEN, CHARSETTD, and
CHARSETFORM. We will examine these clauses in the next sections.

PUSQL vs. C Datatypes


Since PL/SQL and C do not have the same set of datatypes, Oracle8
provides a default C datatype for each pL/SeL datatype. you can override
this default if desired. To facilitate this process, oracie defines a set of
external datatypes, which are defined in Table 20-1. An external datatype is
a mnemonic for a commonly used c type. External datatypes are also used
in Pro*C and oCl programs. oracle also defines additionar external
datatypes which are not standard to c, such as ocrLoBLocAToR. Table
20-2 describes the available external datatypes for each pL/SeL datatype.

External Datatype Description C Datatype


CHARl One-byte quantity, used to char
store either a character or
integer ranging from -128 to
+127.
UNSICNED CHAR1 One-byte quantity, used to unsigned ch.rr
store integers ranging from 0
to 255.
SHORT1,2 Usually a two-byte quantity, short
used to store an inteper
ranging from -215 to"+215 -1.
UNSICNED SHORT1,2 Usually an unsigned two-byte tunsisned shorl
quantity, used to store an
integer ranging frorn 0 to +21('

TABLE 20- l. External Datatytpes


Chapter 20: External Procedurer
793

External Datatype Description C Datatype


lNTl' 2 Usually a four-byte quantity, int
used to store an integer
ranging from -231 to"+231 -1 .
UNSICNED lNTl'2 Usually an unsigned four-byte unsigned int
quantity, used to store an
integer'ranging from O to 232
LONC1'2 Usually a four-byte quantity, long
used to store an inteser
ranging from _231to"+231 -1.
UNSICNED LONC1'
2 Usually an unsigned four-byte unsigned long
quantity, used to store an
integer'ranging from O to 232.
gZE-f1'2 Number of bytes as specified size-t
by the operating system and
compiler.
SB13 Signed one-byte quantity. sb1
UBl3 Unsigned one-byte quantity. ub1
SB23 Signed two-byte quantity. sb2
UB23 Unsigned two-byte quantity. ub2
SB43 Signed four-byte quantity. sb4
UB4 Unsigned four-byte quantity. ub4
FLOAT1'
2 Usually a four-byte quantity, float
used to store floating point
numbers.
DOUBLE1'
2 Usually a four-byte quantity, double
used to store floating point
numbers.
STRINC1 Used to store variable length char *
nu I l-termi nated character
stri ngs.

TABLE 20- 1. External Datatypes (continued)


794 Oracle8 PUSQL Programming

External Datatype Description C Datatype


RAW1 Used to store variable length unsigned char *
blde strings.
OCILOBLOCATOR4 Used in OCI functions to OClLoblocater *
manipulate LOBs in the
database.

TABLE 20-1. External Datatypes (continued/

1A standard C datatype.
2The size of this type can vary, depending
on the operating system and compiler. Thus it is
not always portable.
3An Oracle-defined datatype that is defined in the header
file oratypes.h. Each platform
defines this type such that it meets its definition, ensuring that it is portable.
4An Oracle-defined datatype that is defined in the header
file oci.h. This type is used only
in the OCI functions that manipulate LOBs in the database.

PUSQL Type Supported External Datatypes


CHAR, CHARACTER, LONC, STRINC*
ROWID, VARCHAR, VARCHAR2
BINARY INTECER, BOOLEAN, INTX, CHAR, UNSICNED CHAR,
PLS INTECER SHORT, UNSICNED SHORT,
UNSICNED INT, LONC,
UNSICNED LONC, SB1, UB-I , SB2,
UB2, SB4, UB4, SIZE r
NATURAL, NATURALN, POSITIVE, UNSICNED INT*, CHAR,
POSITIVEN, SICNTYPE UNSIGNED CHAR, SHORT,
UNSICNED SHORT, INT, LONC,
UNSICNED LONC, SB1, UBl , SB2,
uB2, SB4, UB4, SIZE T

TABLE 20-2. PL/SQL and External Datatype Mappings


Chapter 20: External Proceduret 795

PUSQL Type Supported External Datatypes


FLOAT, REAL FLOAT*
DOUBLE PRECISION DOUBLE*
LONG RAW, RAW RAW*
BFILE, BLOB, CLOB OCILOBLOCATOR*

TABLE 20-2. PL/SQL and External Datarype Mappings

* Defau lt external datatype

Each entry in the PARAMETERS clause represents a parameter to the C


external function. For example, consider the OutputString function that we
examined earlier. The wrapper function has a PARAMETERS clause like

CREATE OR REPLACE PROCEDURE Outputstring(


P-PAIh IN VARCHAR2,
p-Message IN VARCHAR2) AS EXTERNAI

P;aMETER' (p-Path srRr^lc,


p_Message STRING),

and the C function is defined with

voj-d Outputstring (path, message)


char *path;
char. *message; {

This clause specifies that p_Path and p_Message are to be passed to C as


null-terminated strings of type char *. Since the default external datatype
for VARCHAR2 is STRINC, the PARAMETERS clause could have been
omitted in this case.
AJternativel/, consider OutputString2, which takes an int parameter.
This parameter indicates the number of times the message should be
repeated. The C function is defined with:

-- .lvaitable ontine aa part of outgtr.c


/* outputs the string contained in message to a fil-e specified by
796 OracleS PUSQL Programming

path. The message is repeated num-ti-mes times. The file will be


created if it doesn't exist. */
void OutsputString2 (path, n€asag€, nusr_timee)
char *path;
char *nessage;
int nun_timesi (
FILE *file_handle;
int counter;
/* Open the file for writinq. */
file_handle = fopen(path, ,,w',);
for (counter = 0; counter < num_times; counter++)
/* Output the string followed by a newline. */
fprintf (fi1e_hand1e, ,'*s\n", message) ;
/* Close the fi1e. */
fclose (file_handle) ;
]

and the wrapper procedure is defined with

-- Available online aa Dart of outstr.sql


CREATE OR REPLACE PROCEDURE Outputstrj_ng2 (
p_Path IN VARCHAR2,
p_Message IN VARCHAR2.
p_Numlines IN BfNARY_INTEGER) AS EXTERNA-L

LIBRARY stringlib
NAME "OutputString2',
PANAUETTRS (p-PaLh STRIIiIG,
D_Message STRIIIG,
g_Nrurlines MT);

Note that p_Numlines specifies INT as the external datatype.

Parameter Modes
The parameters for the wrapper procedure (and hence for the external
procedure) can be of any PVSQL mode-lN, OUT, or lN OUT. lf the
parameter is OUT or lN OUT, then the associated C parameter must be
passed by reference rather than by value, regardless of the datatype. The
only exception to this rule is external datatype srRlNC, which is passed by
reference regardless of the associated parameter mode. OutputString3
Chapter 20: External Proceduret
797

illustrates this. Note that p_NumlinesWritten is OUT, and thus the


associated C parameter is passed as int *, rather than int. The code for the
wrapper and external procedures follows.

/. Available online aE part of outstr.c */


/* Outputs the stri-ng contained in message to a fil-e specj-fied by
path. The message is repeated num_times times. The file wi-1l be
created if it doesn't exist. The number of lines actually
written
is returned in num lines written. */
void Outputstring3 (path, message, num_times, num_1ines_written)
char *path;
char *message;
j-nt num_times;
ub2 tnum linee wrLt,t,en; {
FILE *file_handle;
int counter;
/* open the file for writing. */
fil-e_handle = fopen(path, "w")-
for (counter = 0; counter < num_times; counter++) {
/* Output the strj-ng folfowed by a newline. */
fprintf (file_hand1e, "ts\n", message) ;
( *num-l- i-nes-writ ten ) ++ ;
)

/* Close the file. */


fclose(file_handle);
)

-- AvaLlable online as Dart of outstr.aql


CREATE OR REPLACE PROCEDURE OutputString3 (

p_Path IN VARCHAR2,
p_Message IN VARCHAR2,
p_Numlines IN BINARY_INTEGER,
D_Numlineawritten OIII IiIATI'RAL) AS EXTERNAL

LIBRARY stringli-b
NAME "Outputstring3"
PARAMETERS (p_Path STRING.
p_Message STRING,
p_Nrunlines INT,
p_lihlnlinesWrLtten IIB2 ) ;
798 Oracle8 PUSQL Programming

Parameter Properties
Along with the datatype, the pARAMETERS crause is used to specify
additional information, known as propertie.s, for each parameter. Each
property maps to another parameter in the external procedure, but not the
wrapper procedure. The available properties are described below, and their
supported datatypes in Table 20-3.

lNDlcAToR An indicator is used to specify if the associated parameter


is NULL. PLiSQL variables can be NULL, while C variables cannot. Thus
the indicator is required, since the external procedure may need to know if
a parameter is NULL, or the RDBMS may need to know if a returned
parameter is NULL. The external procedure can check the value of the
indicator-if it is ocl_lND_NULL, then the parameter is NULL, if it is
ocl-lND-NorNULL, the farameter is not NULL. These values can also be
set for output parameters.

LENGTH and MAXLEN LENGTH is used to store the current length,


and MAXLEN the maximum length, of a character or RAW parameter. lf a

Property Supported External Datatypes


INDICATOR SHORT*, INT, LONC
LENCTH INT*, SHORT, UNSICNED SHORT,
UNSICNED INT, LONC,
UNSICNED LONC
MAXLEN INT*, SHORT, UNSICNED SHORT,
UNSICNED INT, LONC,
UNSICNED LONC
CHARSETI D, CHARSETFORM UNSICNED I NT*, UNSICNED
SHORT, UNSICNED LONC

TABLE 20-3. parameter properties

*Defau lt external datatype


Chapter 20: External Proceduret 799

parameter is OUT or lN OUT, then its return value must not exceed
MAXLEN.

CHARSETID and CHARSETFORM These properties are used to


specify the character set lD and form, respectively. They are used in NLS
environments for CHAR, CLOB, and VARCHAR2 parameters. The
equivalent OCI attributes are OCI-ATTR-CHARSET-ID and
OCI ATTR CHARSET FORM.
OutputString4 illustrates the use of properties. The code for the external
procedure and wrapper procedure follows. Note that an indicator is not
necessary for num-lines-written since it will always return a value, and
will thus never be NULL.
/* Available online as part of outstr.c */
/* outputs the string contained i-n message to a file specified by
path. The message is repeated num times times. The file will be
created if it doesn't exist. The number of l-ines actually written
is returned in num-fines-written. The NULL-ness of each
parameter is checked. */
void Outputstring4 (path, path-j-nd,
message, message-ind,
num-times, num-times-j-nd,
num lines written)
char *path;
short palh-ind;
char *message;
Ehor! message-ind;
int num-times;
Ehort nun-times-ind;
ub2 *num-lines-written; {
FILE *file-handl-e;
int counter;
/* If any of the j-nput parameters are NULL, return zero and
don't output anythinq. */
if (path-ind == OCI-rND-NULL | | message-ind == oCf-IND-NULL | |

num_times-ind == OCI-IND-NULL) {
*num-fines-written = 0;
return;
)
800 OracleS PUSQL Programming

/* open the file for writing. */


file_handle = fopen(path, ,,w,,) ;
for (counter = 0; counter < num_times; counter++) {
/* Output the string followed by a newline. */
fprintf (fi1e_hand1e, ,'?s\n", messaqe) ;
( *num_1 ines_writt.en ) ++ ;
)

/* Cl-ose the fi-1e. */


fclose ( file_handle) ;
]

-- Available online as part of outatr.,aql


CREATE OR REPLACE PROCEDURE Outputstring4 (
p_Path IN VARCHAR2,
p_Messag'e IN VARCHAR2,
p_Numlines IN BfNARY_INTEGER,
p_Nunl,ineswritten OUT NATURAL) AS EXTERNAL

LIBRARY stringlib
NAME "Outputstring4"
PARAMETERS (p_Path STRTNG,
p_Path IIIDICATOR,
p_Message STRING.
p_Mes5age INDfCATOR,
p_Nurnlines TNT,
p_ilnrmlJin€3 MDIeATOR,
p_Numlineswritten UB2 ) ;

External Functions
and Packaged Procedures
ln addition to external procedures, you can create external functions.
External procedures and functions can be contained in packages, and thus
take advantage of packages features such as overloading.

Function Return Values


An external function is created just like a procedure. The return value of the
function is specified using the keyword RETURN in the PARAMETERS
clause. Properties can be associated with the return value as well, as
OutputString5 shows:
Chapter 20: External Proced'ure. g0 I

/* Available online aE part of outstr.c */


/* outputs the string contai-ned in message to a file specified by
path. The messag,e is repeated num_times times. The file wil_1 be
created if it doesn't exi-st. The number of lines actually
written is returned. The NULL_ness of each parameter
is checked. */
ub2 Outputstring5 (path, path_ind,
message, message_ind,
num_times, num_times_ind,
retval_ind)
char *path;
short path_ind;
char *message;
short message_ind;
int num_times;
short num_times_ind;
short *retval_ind,. {

FILE *fife_handle;
ub2 counter;

/* If any of the input parameters are NULL, return NULL and


don't output anything'. */
if (path_ind == OCr_rND_NUI-L | | message_ind == OCr_rND_NULL
I
num_times_i-nd == OCT IND -NULL) i
I

*retvaf_ind = OCI_IND_NULL;
return O;
]

/* Open the file for writing. *i


file_handle = f open (path, "w,' ) .
for (counter = 0; counter < num_times; counter++) {
/* Output the strinq foflowed by'a newline. */
fprintf (file_handle, ',Bs\n,', message) ;
)

/* Close the file. */


fclose ( file_handle) ;

/* Set up return vafues. */


*retval_ind = OCI_IND_NOTNULL;
return counter,.
802 OracteS pUSeL programming

-- Available online as part of outetr.sql


CREATE OR REPI,ACE FUIJCTIOItrOutputstring5 (
p-Path IN VARCHAR2,
p-Messaqe IN VARCHAR2,
p_Num],ines fN BINARY_INTEGER)
RETURN NATURAL AS EXTERNAL

LIBRARY stringllb
NAME',Outputstring5,,
PARAMETERS (p-Path STRTNG,
p_Path TNDICATOR,
p_Message STRING,
p_Message INDfCATOR,
p_Numlines fNT,
p_Numl,ines fNDICATOR,
RETURN TIUDTCATOR,
RETI'RN T'82 ) ;

Overloading
Wrapper procedures (or functions) can be in packages,
as weil as
standalone. rn this case, the wrapper procedure
,r..'o"rri uu in ,rru package
body, with a forward decraration in the package
he"aer, as normar. The
debug-extproc package rater in this chapter iriustrates
tnirl"inniqr".
RESTRICT_REFERENCES
/ you can assert the
llow it to be used in a SeL statement.
' il:l ;?"",j','J5,l:;,
external
ffi;Eiil,,
function violates the
pragma, internar errors can resurt.

Callbacks to the Database


An.external procedure can make cailbacks to
the database, to raise errors
or issue sQL commands. These are done using the
oracre8 ocr in,urfu.u.
All sQL statements executed from an external"procedure
connection and rransaction established in the pLlSeL
,r" ,r," existins
*rupp", ;r;.";;r:
Chapter 20: External procedude, g03

Service Routines
service routines are used to raise exceptions in the database, allocate
memory, and retrieve OCI handles to execute SQL statements. All of
the
service routines take a context as one of their parameters. ln C, the
context
is of type oClExtproccontext, and is indicated by the coNTEXT
keyword
in the parameter list. The WlrH CONTEXT clause is also required.

OClExtProcRaiseExcp
This service routine raises a piedefined exception, similar to the
RAI5E
statement. The C prototype is defined with

int OCIExtprocRaiseExcp (with_context, error_number)


OClExtprocContext *wi th_context ;
size_t error_number;
Like RAlsE, when you cail oCrExtprocRaiseExcp, no varues are
assigned
to oUT or lN oUT parameters, a rd the procedure must return
immediately, outputstring6 illustrates this service routine by raising
oRA-6502 if a NULL value is passed. Note the use of wtrH coNr"rxT
in
the PARAMETERS clause.

/* Available online as part of outstr.c */


/* outputs the string contained in message to a fir-e specified
path. The message is repeated num_tirnes times. The by
file will be
created if it doesn,t exist. The number of lines actually
written is returned. The NULL_ness of each parameter is
checked, and oRA-6502 i-s raised if any input parameter is
NULL. * /
ub2 Outputstring6 (context, path, path_ind,
message, messaqe-ind,
num_times, num_times_ind,
retval_ind )

OcfExtProccontext *context i
char *path;
short path_ind;
char *messag.e,.
short message_ind;
int num times;
short num_times_ind;
short *retval_ind; {

FILE *file_handle;
804 O;acle8 PUSQL programming

ub2 counter;

/* If any of the input parameters are NULL, raise


return immediately. * / ORA-6502 and
if (path_ind == OCI rND_NUr_r, message_ind
|| == oCI_IND_NULL |
num_tirnes_ind == OCr_IND_NULL) |

OCIExtprocRaiseExcp ( context, 6502)


i
;
return 0;
]

/* Open the fife for writing. */


file_handle = fopen(path, "w,)_
for (counter = 0,. counter < num_times,. counter++)
/* Output the string followed by a newline. */
fprintf (file_handf e, ,,tss\n,,, rnessage)
;
)

/* Cfose the fi1e. */


fclose(fife_handle);
/* Set up return values. */
*retval_ind = OCI_IND_NOTNULL;
return counter;
)

-- Available online aa par! of outstr.sql


CREATE OR RE|LACE FUNCTION Outputstring6 (
p_Path rN VARCHAR2,
p_Messag-e rN
VARCIiAR2,
p_Numl,ines IN
BfNARY._fNTEGER)
RETURN NATURAL AS EXTERNAL

LIBRARY stringlib
NAME,'OutputString6,,
WTTH COIITEKT
PARAMETERS (COTEEXT,
p_path STRTNG.
p_path INDTCATOR,
p_Message STRING,
p_Message INDfCATOR,
p.Nr.unlines fNT,
p_Numlines fNDfCATOR,
RETURN INDICATOR,
RETURN UB2);
Chapter 20: External Procedurer
805

OC I ExtProcRaiseExcpWith Msg
This service routine is similar to RAtsE_APPLlcATloN_ERRoR. Unlike
oClExtProcRaiseExcp, it allows you to pass a user-defined message along
with the error. The error number should be between 20,000 and )o,ggg,
just like RAISE_APPLICATION_ERROR. The protorype is

int OCIExtProcRaiseExcpWj_thMsg (
with_context, error_number, error_message, 1en)
OclExtProccontext *with context;
size t error_number;
text *error_messaqe;
size t 1en;

lf error-message is a null-terminated string, then len should be zero.


otherwise, len should be the length of the string. outputstringz (in the
next section) illustrates this technique.

OC I ExtprocAl locCal I Memory


lf you need to allocate memory in your external procedure, you can use
OClExtprocAllocCallMemory. This routine will allocate memory that will
last for the duration of the call, and will be freed auromarically by pLlseL
when the external procedure returns. The prototype is

dvoid *OCIExtprocAf locCal-lMemory (rnrith_context, amount ) ;


OClExtProccontext *with memory;
size t amount;

where amount is the number of bytes to allocate. OutputStringT uses this


routine to allocate memory for the error message text.

/* Available online as part of outstr.c */


/* Outputs the string contained in message to a fife specified by
path. The messaqe is repeated num_times times. The file will- be
created if j_t doesn,t exist.. The number of lines actually
written is returned. The NULL-ness of each parameter is
checked, and ORA-6502 is raised if any input parameter is
NULL. If the fife cannot be opened, a user-defined. error j-s
returned. */
ub2 OutputStringT (context, path, path_ind,
message, message_ind,
num_tj-mes, num_times_ind,
retval_ind)
806 Oracle8 PUSQL programming

OCf ExtProcContext *context,.


char *path;
short path_ind;
cnar *message;
short messagre ind;
l-nt nurn_times;
short num_times_i-nd;
short *retval_ind; {

FfLE *file_handle;
ub2 counter,.

/* rf any of the input parameters are NULL, raise oRA-6502 and


return immediately. * /
if (path-ind == ocr-rND-NULL I i message_i-nd == oCr_rND NULL I I

num_times ind == OCI_IND_NULL) {


OCIExtProcRaiseExcp (context , 6502) ;
return O;
)

/* Open the file for writing. *7


file_handle = fopen(path, ',w', ) ;
/* Check for success. If not, raise an error. */
if (!file_handle) t
text *initial_msg = (text *),'Cannot, open file ,,;
eext *error_msgi
/* Allocate apace for the error meEsage texE, and set it up.
We do not have to free this rnernory _ pL,/SeIJ wi1l do that
autornatically. * /
error_mgg = OCf ExtprocAl Locca1lMemory ( context,
strlen(path) + strlen(initial_msg) + 1);
strcpy( (char *)error_meg, (char *)initial_neg; ;
st,rcat ( (char * ) error_rns9, path) ;

OcfExtProcRaiseExcpWittr.l.{sg(context, 2OOO1, error_ms9, 0)


;
return 0i
)

for (counter = O; counter < num_tirnes,. counter++) {


/* Output the string fofl-owed by a newli-ne. */
fpri,ntf (f ile_hand1e, "%s\n", messagre) ;
)
Chapter 20: External Procedurer
807

/* Close the fi-fe. */


fclose ( file_handle) ;

/t SeL up return values. */


*retval_ind = OCT_IND_NOTNULL;
return counter;
]

Executing SQL in an External Procedure


The oracles ocl is used to execute sQL statements from an external
procedure. ln order to use the OCl, you need to set up several OCI handies.
The OciExtProcCetEnv routine is used to do this.

OciExtProcGetEnv
This service routine is defined with

sword OciExtProccetEnv(with_context, envh, svch, errh)


OCfExtProcContext *with context;
OCIEnv * *envh,'
OCfSvcctx **svch;
OCIError **errh;

You can use the returned environment, service context, and error handles
in subsequent OCI calls that execute SQL statements. These handles can be
used only for callbacks-they cannot be used for standard OCI calls. For
more information on the oCl handles and their use, see lhe programmer,s
Guide to the Oracle Call tnterface.

Restrictions
There are several restrictions to keep in mind when issuing sel statements
within an external procedure. The following kinds of statements are not
allowed:

I Transaction control commands like COMMIT or ROLLBACK


I DDL commands which would cause an implicit COMMIT
I Calls to other external procedures
808 OracteS pUSeL programming

ln addition, there are certain oCr


calrs which are not ailowed in
external procedure. see the aracreB pL/seL an
for a complete list.
uruir-c,liaJ'ura Reference

Tips, Guidelines, and Restrictions


ln this last part
of the chapter, we wi, discuss.some
explore guidelines and restrictions debugging tips and
for external procedures.

Debugging External procedures

ntrol of a debugger. The solution


to
dummy externif procedure) and
the debugger.
Chapter 20: External Proceduret 809

NOTE
ln order to use this method, your operating
system must support a debugger that allows
yott to attach to a running process. For
example, on many Unix systems you can
use the gdb debugger.

To facilitate this process, Oracle provides a package called


debug_extproc. The script to create this package can be found in the
PL/SQL demo directory under $ORACLE HOME and also on the CD
accompanying this book. The code for debug_extproc is as follows:

-- Availab1e online as dbgextp.sql


CREATE OR REPLACE PACKAGE debug_extproc IS
-- Start up the extproc agent process in the session
Executing this procedure starts up the extproc aEent process
in the session allowing one to be able get the PfD of the
executlngr process. This PID is needed to be able to attach
to the running process using a debugger.
PROCEDURE startup_extproc_agent ;
END debug_extproc;

CREATE OR REPLACE LIBRARY debug_extproc_library IS STATIC;

CREATE OR REPLACE PACKAGE BODY Cebuqr extproc TS


extproc_ I ib-error EXCEPTf ON ;
PRAG}/LA EXCEPTION_INIT (extproc_fib_er-ror, -6520) ;

extproc_func_error EXCEPTITIN ;

PRAGItrA EXCEPTION_INIT (extproc f uric er-r-or:, - 6 521) ;

PROCEDURE locaI startup_e\tproc aqent IS EXTERNAL


LIBRARY debug-ex1-proc-1 ibrar\' ;

PROCEDURE sfartup_extproc ageltt ls


BEGfN
-- call a dummy proceCure and 't r.ap alf errors.
f ocal_s tartup-extProc-agent ;
EXCEPTTON
-- Ignore any errors if tl-re function or library.is not found.
extprcc-fltnc- err.ri
'lVliEN --hell ]ilil.L:
TiTHF\r
rvl IL]v ovtDrCC
! .\ ( I lib el t ar L]-,Ctl )Jiri i. '
810 OracleS PUSQL Programming

END startup_extproc_agent ;
END debug_extproc;

The steps for using this package are as foliows:

t. Make sure that your C program is compiled and linked into the
shared library with debug symbols. on Unix systems, this is usually
done by passing the -g flag to the compiler.
2. ln a SQL*PIus or other connection to the database, call
debug_extproc"startup_extproc_agent, Although there is no actual
C procedure associated with this wrapper procedure, extproc is
started. No errors are signaled because of the exception handlers in
startu p_extproc_agent.
3. Determine the operating system process lD of extproc. On Unix,
this can be done with a ps command.
4. Start the debugger, and attach to the running process determined in
step 3.

5. Set a breakpoint in the function "pextproc," and allow the debugger


to continue.
callthe wrapper procedure. This will in turn
5. ln the original session,
call extproc, which will then stop rn pextproc.
7. At this point, the external procedure has been resolved by extproc,
so you can set a breakpoint for it. As the symbol will have been
dynamically loaded, some debuggers may not immediately
recognize it, and you may have to tell the debugger to load the
symbols again. (This is done with the share command in gdb.)
8. Continue in the debugger, which will then break in your procedure.
You can now step through the external procedure code and debug.

Guidelines
There are several things to keep in mind when using external procedures:

I Calling an external procedure does involve some overhead,


especially the first time in a session. A request has to be made to the
listener, and extproc needs to be spawned. Therefore, use externar
Chapter 20: External Proceduret I I I

procedures when the effrciency gained by coding in C outweighs


the cost of spawning the process.
I extproc will assume the operating system privileges of the listener
that spatvned it. Consequently, it is a good idea to staft a separate
listener for external procedures from an account with restricted
access. lf the listener is running as the Oracle user, for example,
then an1, user with permisston to create a library can potentrally
write a C program that could rnodify tlre files under
$ORACL E-HOME, possi bly' corrupti ng the database.
I Be sure to write a value to any OUT or lN OUT parameters, as well
as thefunction return value. PL/SQL has no means of checking this,
and intei'nal errors can result if these parameters are not assigned.
I Likewise, do not write to lN parameters or overflow the capacity (as

specified by the MAXLEN property) of OUT parameters.


I Use the LENCTH and INDICATOR properties to handle all cases of
character paranreters. lf ;'s, ur" returning a NULL value, then set
the LENCTH parameter to zero.
I lf there is a PRACMA RESTRICT REFERENCES statement for the
wrapper function, the external function cannot violate the pragma.
PL/SQL has no way of checking this, and internal errors can result if
the external function nrodifies the database in violation of the
praSma.

Restrictions
Currently (as of Oracle8 version 8.0) exterrral procedLtres have the
fol lowi ng restrictions:

I lf the underlying platfortl does not support dynarlic lirrking .rrrd


shared libraries, then extern.rl procedur€'S .lr€ trot available.
I The SQL*Net listener and extproc nrLrst rLnr ctn the s.rnre rrrachine as
the datab.rse. Sinrilarly, )-oLt carrrrot use.r database lirrk irr the
LIBRARY clause to s;:ecif1' .r libr.trl' otr .l rellote d.rt.rbase.

t The only language curretrtlt'sLrpporled is C. llyoLl w'rrrt to c.rll


another langt-t.tge, however, voLt c.ltll'rite.t C ertetrr.tl procedttre
812 OracteS pUSeL programming

T Parameters cannotb ecords, collections,


or object typeinstan eve these objects
t
by using a callback al procedure'.
I The Oracle server cannot be running MTS. extproc needs
a
dedicated server process with which.-to communicate.
T

Summany
ln this chapter, we have discussed externar procedures,
which ailow a
rocedure. We discussed the issues
ping pL/SeL datatypes to C
also reviewed several methods for
will examine
another new oracres feature, Loa, tr"lg"J[l]:$:'we
.ff.
I I6 OracleS PUSQL programming

feature of any relational database, Iarge objects


,:1*l
(LoBs) are used to store a rarge amount
of da"ta, eiiher text
or binary in nature. ln this chapter, we will explore the
features that Oracle8 hasto support LOBs, including
storage both in and out of the database, different
tyie, of
large objects, and th; pLlSeL interface for manipulating
LOAs_iAI
DBMS_LOB package.

What Is a LOB?
A LoB is s_imply a database fierd that holds a rarge amount
of data, such as
file or long rexr documenr. What oracL datatype is besr
1 Frphi: ro store
this kind of information? ln OracleT, AR2 field can hold up to
can hold up to 2 gigabytes,
such as the fact that there
atabase table. ln addition, the only
ONC RAW data piecewise is the
racle 7.3.
oracle8 provides a solution. LONC and LoNC RAW corumns
are
still available (with the same restrictions), but there is a new
flltype.tamily available-rhe LoB famiry. There are four different kinds of
LoBs, which are designed for different kinds of data:
cLoB, Ncloe,
BLOB, and BFILE' These datatypes are described in
Tabre 2i-i.
since the data is stored within the database, CLoBs,
NCLoBs, and
BLoBs are coilectively known as internal LoBs. BFTLEs
are kn'wn as
external LOBs. LOBs have the following characteristics:

r The maximum size of a LoB is 4 gigabytes, rather than


the oracreT
limit of 2 gigabytes for LONC unj LONC RAW data.
I LOBs can be manipurated using either the oracre8
Cail rnterface or
through PLlSeL with the DBMS_LOB package. goth of
these
interfaces provide random to a LoB for both reading and
".."*
writing (except for BFlLEs, which are read_only).
I The restrictions on LONC br LoNC RAW data do not
appry to
LoBs. For example, unrike LONC or LoNC RAW corumns,
there
can be an unlimited number of LoB columns (up to
the maximum
number of columns) in a given database tabre, and LoB
data can be
manipulated in triggers.
Chapter 2l: Large Objects 8 | 7

Objects can have LOB attributes, and methods can take LOBs as
arguments. However, an object cannot have an NCLOB attribute,
although a method can take an NCLOB argument.
I LOBs can be used as bind variables.

I lnternal LOBs are manipulated using either SQL DML statements or


the DBMS_LOB package. Either way, there is full transactional
support and read consistency, just like non-LOB data.

LOB Storage
Unlike LONC or LONC RAW data, LOB data is not stored inline in a
database table. Rather, it is stored in a separate location, and a IOB locator
is stored in the original table. The locator serves as a pointer to the actual
data, which can be stored in a different tablespace with different storage
parameters from the table, if desired. The storage for the LOB data can be
specified in the CREATE TABLE statement using the LOB storage clause.
This clause can specify a different tablespace and/or ditferent storage

LOB Type Description


CLOB Similar to the OracleT LONC type, a CLOB can hold
si ngle-byte character data.

NCLOB An NCLOB stores fixed-width multibyte national character


set data.
BLOB Similar to the Oracle7 LONC RAW type, a BLOB can hold
unstructured binary data. Oracle8 does not interpret this data
but can store and retrieve it piecewise.
BFI LE BFILEs allow read-only access to large binary files stored
outside the Oracle database. Unlike the other three LOB
types, BFILE data is stored in a separate file that is not
maintained by Oracle. Consequently, manipulations to BFILE
data do not participate in Oracle transactions; they occur
asynchronously and cannot be committed or rolled back.

TABLE 2l- l . LoB Types


818 OracleS PUSQL Frograrnming

parameters, if desired. This flexibility allows you to manage LoB clata


efficiently. The LOB storage clause is defined with

l-OB (/ob_colurnn, lob column, ...) STORE AS


I Io b_se g n a m e] lsto r age_c I a u s el

where lob column is a table column of a LoB datatype, lob_segname is the


name of tl-re LoB segment in the desired tablespace, and storage__clause
specifies the tablespace and other parameters. For more information on the
LoB storage clause, see the oracle7 Application Developer,s Guide and
Ihe Oracle9 Server SQL Reference
For example, consider the lobdemo table, created with the
following statements:
--Availab1e online as part. of tablesg.sql
CREATE TABLE lobdemo (
Key NUMBER PRIMARY KEY,
cl_ob_cof CLOB,
blob_col BLOB,
bfile cof BFILE
);
The actual data for the each of the LOB columns is stored separately from
the rest of the data in lobdemo. This is illustrated in Figure 21-.1.

NOTE
BFlLEs, like CLOBs, BLOBs, and NCLOBs,
are also manipulated through LOB locators.
However, the storage for BFILE data is in a
file outside Oracle's control. For more
information on BFlLEs, see fhe section
"Manipulating BFlLEs" later in this chapter.

LOBs in DML
DML statements are used to manipulate entire LoBs. you can UpDATE a
LoB to a new value, for example. LoBs can be manipulated piecewise
using the DBMS-LOB package, described in detail later in this chapter, in
the section "DBMS-LOB Routines". All of the DBMS LoB routines use a
LOB locator to manipulate the data.
Chapter 2l: Large Obiects I |9

lobdemo

Key clob col blob col bfile col

6f"bd;D
\@ )
(
=---:>/

blob data

FIGURE 2l- l . LoB storage in the database

lnitializing a LOB Column


A LOB column can be set to NULL, as in the following INSERT statement:
INSERT INTO lobdemo(key, clob-col, bfob-cof, bfile-co])
VALUES (10, NULL, NULL, NULL);

ln this case, a NULL value is inserted in the column, rather than a LOB
locator. There is no actual storage allocated for the lob data, since there is
no locator to point to it. Because of this, you cannot use DBMS-LOB on a
NULL value. The row must be updated to a valid locator first.
One way of doing this is with the EMPTY-BLOB0 and EMPTY-CLOBO
functions. These functions take no arguments, and return a valid LOB
locator. DBMS-LOB can then be used to populate the LOB data. For
example, consider the following INSERT statement:
820 OracleS PUSQL Programming

INSERT INTO l_obdemo(key, clob_co1, blob col-, bfile_co1)


VALUES (11, empty_clobO, empty_b1obO, NULL);

NOTE
The empty parenthesis are required for
EMPTY_BLOB0 and EMPTY CLOB0.

After executing the above two statements, the lobdemo tabie will look
like Figure 21-2. After the column has been initialized, you can sELECTthe
lob locator into a PL/SQL variable and use it with DBMS LOB.
Alternativelf , you could use the RETURNINC clause on the INSERT to
retrieve the locator directly.
BFILEs are not initialized with EMPTY_CLOB() or EMpTy_gLOBfl;
rather, the BFILENAME function is used instead. This is described in the
fol lowing section, "Man ipulating BFI LEs".

Example
The follor,ving example illustrates some DML statements which manipulate
LOBs. Note that without DBMS_LOB, only the entire LOB can be

lobdemo

Key clob col blob co bfile col


10 NULL NULL NULL
11
a a NULL

c_>o
FIGURE 2l-2. lobdemo table after insefts
Chapter 2l: Large Objects 82 I

referenced. Also, a SELECT from a LOB column will return the locator, not
the data itself.

-- Available online as J.obdml.sql


DECLARE
v_CLOBfocator CLOB;
v_BLOBlocator BLOB;
BEGIN
-- Initiafizes the clob co1 to the specified string, and returns
-- the locator into v LoBI-ocator.
INSERT INTO lobdemo (key, clob_col)
VALUES (20,'abcdefghijkJ-mnopqrsturz.wxyz, )
RETURNING cfob_co1 INTO v_Cl,OBfocator;

-- Modifies blob_col for the same row.


UPDATE l-obdemo
SET blob_co1 = HEXTORAW( ,00FF00FF00FF, )

WHERE key = 26-

-- Retrieves the locator for the newly updated value, not the
-- value itsel_f .
SELECT blob col-
INTO v_BLOBlocator
FROM ]obdemo
WHERE key = 26'
END;

Manipulating BFILEs
The main characteristic of BFILEs is that they are stored outside the
database. Like other kinds of LoBs, you use a locator to manipulate the file.
However, there are several things to note about BFILEs:

I Because the file is maintained outside of Oracle, there is no


transactional control for BFILE manipulations. Oracle does not
provide support for integrity or durability of these external files,
so this must be supplied by the underlying operating system.

! BFILEs are read-only. Both the DBMS_LOB package and the


OracleB OCI can be used to read the files, but they cannot
modify them.
822 OracleS PUSQL Frogramming

f As of Oracle8 Release 8.0, session migration in MTS


(multi-threaded server) mode is not supported for BFlLEs. Thus open
BFILEs cannot persist beyond the end of a callto an MTS server, so
the session needs to be bound to one shared server to process them.

Directories
ln order to access an external file, the oracle server needs to know the
location of the file on the operating system. This is done through a new
type of data dictionary object called a directory. The directory is a logical
alias for an actual file system path. A directory is created with the CREATE
DIRECTORY starement:

CREATE DIRECTORY dir name AS os_piath;

where dir name is the name of the new directory, and os_path specifies a
cornplete path of a directory on the operating system. For example, the
following statement creates a directory which points to the operating
system di rectory "/home/uti ls":

CREATE DIRECTORY utils AS ,/home/uti1s, ;

Privileges Required for Directories


ln order to issue the CREATE DIRECTORY command, you must have the
CREATE DIRECTORY system privilege. This privilege is granred by default
to the DBA role, but can be granted to other users by the database
administrator. Once you have created a director.y, you can CRANT the
READ privilege on it to another user. This privilege is checked by both the
DBMS LOB and the OCI interfaces.
Furthermore, the operating system directory specified must exist, and be
readable by the oracle user. lt is the system administrator's responsibility to
insure this.

Directories in the Data Dictionary


The dba-directories and all_directories data dictionary views describe the
directories that have been created on the system. Dha_directories contains
information about all directories in the database, and all_directories
Chapter 2l: !-arge Obiects 823

contains information about all directories which the current user can
access. The structure of these views is described below.

Column Datatype Description


OWNER VARCHAR2(30) Database schema which
owns the directory
DIRECTORY NAIV1E VARCHAR2(30) Name of the directory
DIRECTORY PATH VARCHAR2(4OOO) Operating system path to
which the directory points

Dropping Directories
Directories are dropped with the DROP DIRECTORY command:

DROP DIRECTORY dir-name;

where dir-name is the directory to be dropped. You must have the


DROP ANY DIRECTORY system privilege to issue the DROP
DIRECTORY command.
Care should be taken when dropping directories. lf a session currently is
reading from a file specified by a directory when that directory is dropped,
LOB operations for that session lvill fail. Furthermore, the resources
associated with the operations will not be released until the session is
killed, or uses the DBMS-LOB.FILECLOSEALL procedure (described in the
section "DBMS-LOB" later in this chapter).

Opening and Closing BFILEs


Operations on BFILEs are very similar to UTL-FILE (described in Chapter
1B). There are DBMS-LOB routines for opening, closing, and reading from
a BFl,LE. Similarto UTL-FILE manipulations, you should be sureto close all
BFILEs opened in a given procedure, even if an exception is raised.
Furthermore, there is a limit on the maximum number of BFILEs which
can be opened by any one session. This is specified by the
SESSION MAX OPEN FILES init.ora parameter. The default value is 10,
which means that no session can have more than 10 files open
simultaneously. The limit can be increased by altering
SESSION MAX OPEN FILES in the init.ora database parameter file.
824 Oracte8 pUSeL programming

BFILEs in DML
similarto internar LoBs, BFTLEs can be manipurated in
DML. However, in
order to do anything significant (such as reading piecewise
from the file)
you must use the DBMS-LOB package or the dcr interface.

lnitializing BFILE Cotumns


BFrLE columns can be initiarized to NULL,
as we saw earrier in this
chapter. rn order to initialize them to a valid iocator,
however, you must
use the BFILENAME function, defined with

FU NCTTON B Ft LENAME (d i rectory_a/ias I N VARCHAR2,


filename tN VARCHAR2)
RETURN BFILE;

where directory-alias is the name of a previousry created


directory
object, and filename is the name of an operating ,yrau,
file. BFTLENAME
can be used in an INSERToT UPDATE statemeriq orto
ipitialize a BFILE
PLISQL variable.
BFILENAME performs no checks to see if the directory
and/or file
actually exist-that check is done when the locator is
accessed with
DBMS-LOB. For exampre, the foilowing TNSERT statement
wiil create a
LOB locator poinring to the file /home/utilsffilel:

INSERT INTO lobderno (key, bfile_cot)


VALUES (_1, BFTLENAME(,utils,,,fite1,
));

Copy vs. Reference Semantics


suppose we create a second tabre with the same structure
as robdemo,
as follows:

-- Availabte online aE part of lablesg.sql


CREATE TABLE ]obdemo2 (
Key NUMBER PRIMARY KEY,
clob_cof CLOB,
blob_cof BLOB,
bfile_col BFILE
);
Chapter 2l: Large Objects 825

and then issue the following SQL statement:

INSERT INTO lobdemo2


SELECT key, clob_cof, blob_cof, NULL FROM lobdemo;

This statement will copy both the locators and the data from lobdemo to
fobdemo2. This behavior is known as copy semantics, since there will be
two sets of the data after the operation. Copy semantics apply to DML
operations on internal LOBs-DML operations on external LOBs use
reference semantics. With reference semantics, only the LOB locator is
copied. For example, consider the following PL/SQL block:

-- Availa.ble online as bfiledrnl .sq1


BEGIN
-- Create a row in lobdemo pointing to an external file.
INSERT INTO lobdemo (key, bfile-co])
VALUES (5, BFILENAME('util-','fl]e1') ) ;

-- Copy thj-s focaior into lobdemo2.


INSERT INTO lobdemo2
SELECT *
FROM fobdemo
WIiERE keY = 5'
END;

After executing the above biock, both lobdemo and lobdemo2 will
contain a row that points to the same operating system fiie. This situation is
illustrated by Figure 21-3.

Deleting BFILE Locators


When you delete a row containing a BFILE locator, only the locator is
deleted. The file itself still remains, and if other locators point to it can still
be accessed . lf , for example, we execute the following DELETE statement
after executing the above block:

DELETE FROM lobdemo


WHERE keY = 5'

then filel can still be accessed from the locator stored in lobdemo2.
826 OracteS pUSeL programming

lobdemo

Key clob_col blob bfile col


5 NULL NULL
\

FIGURE 2l-3. lobdemo and lobdemo2

The DBMS_LOB package


with sQL, you can only manipulate the entire LoB; you cannot manipulate
pieces of the LoB data. The DBMS LoB package rectifies
this by allowing
piecewise manipulation (read and write for inteinar LoBs,
and read
only for external LoBs) of LoB data. This section describes
the DBMS LoB
routines in detail, and also the interaction between DBMS
LoB and
DML manipulations.

DBMS_LOB Routines
Five subprograms in DBMS_LoB are used to read LoB
values or return
information about a LoB value. None of these routines modifies
a LoB
value. The routines are

COMPARE
CETLENCTH
Chapter 2l: Large Objects g}l

INSTR
READ
SUBSTR

ln addition, DBMS_LOB contains six subprograms that write


to LOB values.
These are

APPEND
COPY
ERASE
LOADFROMFILE
TRIM
WRITE

Finally, the following six routines can be used for manipulating


BFILEs:

FILECLOSE
FILECLOSEALL
FILEEXISTS
FILECETNAME
FILEISOPEN
FILEOPEN

All of the D described in the following sections,


in alphabetical escriptions is a list of the &.uption,
which can be r . The examples assume the lobdemo
table has been ted with the foilowing script:
-- Available online as lobtest.sql
INSERT INTO l_obdemo (key, clob_col, bl_ob_co1, bfile
col)
VALUES (_1, EMPTY-CLOBO, EMPTY-BLOBO, NULL);
fNSERT INTO lobdemo (key, cfob_col, blob_co], bfife_col)
VALUES (l,,abcdefghijklmnopqrstv\twx.j,z,,
HEXTORAW (' OOOl 02 O3 04 O5O 607 OB O gOAOBOCODOEOF'
),
NULL) ;
INSERT TNTO lobdemo (key, cfob_cof, blob_col, bfife_col)
VALUES (2, ,A euick Brown Fox Jumps Over the Lazy Dog,,
HEXTORAW ( ' FFFEFDFCFBFAFgFBFTF6F5F4F3 F2F1FO ' ) ,
NULL) ;
COMMIT;
828 Oracte8 pUSeL programming

For more information on the DBMS-LOB


package, see the oracre,
Server Appl ication Developer's Guide.

APPEND
Use this routine to append the contents of
a source LoB to the destination
LoB' The source LoB^': unchanged. AppEND is
overroaded to accept
binary or character LOBs with th"e following
definitions:

PROCEDURE APPEND(
dest_lob tN OUT BLOB,
src_lob tN BtOB);

PROCEDURE APPEND(
dest_lob tN OUT CIOB CHARACTER SET
ANy_CS;
src lob tN cLoB CHARACTER sET dest-loby"cH-ARSET);

Here, desf /ob is the destination LoB, and src_robis


the source LoB. Note
that APPEND cannot be used for BFrLEs, since
they are read-onry. The
VALUE ERROR exception is raised if either src_robor
dest_robis NULL.
(The complete list of exceprions
raised uv
t ogNas_fog ,"r;"", can be
found at the end of this section.)
NOTE

%CHARSET attribute returns the character


set of its argument, and insures that both
src_lob anddest_lob are in the same
character set.

COMPARE
This function is used to compare two entire
LOB values or parts of two LOB
values. lt will return zero if the LOB values are
identical or non-zero
if they
are not. COMPARE is overloaded to accept
all four types of LOBs with the
fol lowi ng defi n itions:
Chapter 2l: Large Objects 829

FUNCTION COMPARE(
lob_l lN BLOB,
lob_2 tN BLoB,
amount lN INTECER := 4294967295,
.1,
offset_/ lN INTECER:=
offset_2lN INTECER := 1)
RETURN INTECER;
PRACMA RESTRICT_REFERENCES(compare, WNDS, RNDS, WNPS, RNpS);

FUNCTION COMPARE(
lob_l lN CIOB CHARACTER SET ANy CS,
lob_2 lN CIOB CHARACTER SET lob_l%CHARSET,
amount lN INTECER := 4294967295,
offset_/ lN INTEGER:= 1,
offset_2lN INTECER := .l)
RETURN INTECER;
PRACMA RESTRICT-REFERENCES(compare, WNDS, RNDS, wNps, RNps);

FUNCTION COMPARE(
file_l lN BFILE,
file_2 lN BFILE,
AMOUNT IN INTEGER,
offset_/ lN INTECER:= 1,
offset_2lN INTECER := 1)
RETURN INTECER;
PRACMA RESTRICT_REFERENCES(compare, WNDS, RNDS, WNPS, RNpS);

Note that COMPARE can only be used to compare to LoB values of the
same type, such as two BLoBs, two BFlLEs, and so on. The parameters and
return value for COMPARE are described in the foilowing table:

Parameter Datatype Description


lob_l, file_l BLOB, CLOB, LOB locaror for the first LOB to
NCLOB, BFILE be compared.
lob_2, file_2 BLOB; CLOB, LOB locator for the second LOB to
NCLOB, BFILE be compared.
830 OracleS PUSQL programming

Parameter Datatype Description


amount INTECER Maximum number of characters
(CLOB, NCLOB) or bvtes
(BLOB, BFILE) to compare.
offset 1 INTECER Offset in characters (CLOB, NCLOB)
or bytes (BLOB, BFILE) into rhe first
LOB before starting comparison. The
first byte or character has offset 1.
offset 2 INTECER Offset in characrers (CLOB, NCLOB)
or bytes (BLOB, BFTLE) into the
second LOB before starting
comparison. The first byte or
character has offset 1.
return value INTECER Zero if the two LOB values are
identical, non-zero if not. A NULL
value is returned if amount, offset_l,
or offset_2 is < 1 or > LOBMAXSIZE.

COPY
coPY is used to copy all or part of a source LoB to a destination LoB.
rf
the specified offset for the destination LoB is beyond the enJ
of the data
currently in this LoB, zero-byte characters (for tilogr) or spaces (for
cLoBs and NCLoBs) are inserted. lf the destination offset is less
than the
current length of the destination LoB, the current data is overwritten.
lf the
specified amount is greater than the length of the source LoB,
then the
entire source LoB is copied and no erroi is raised. copy
can be used to
copy data between like LOBs only. lt is defined with

PROCEDURE COPY (

dest_lob tN OUT B[OB,


src_lob lN BLOB,
amount lN INTECER,
dest_offset lN INTECER:= 1,
src_offset lN INTECER := 1);
Chapter 2l: Large Objects 83 |

PROCEDURE COPY (

dest_lob lN OUT CLOB CHARACTER SET ANY_CS,


src_lob lN CLOB CHARACTER SET desf_lob%CHARSET,
amount lN INTECER,
dest_offset lN INTECER := 1,
src_offset lN INTECER := 1);

The parameters for COPY are described in the following table:

Parameter Datatype Description


dest_lob BLOB, CLOB, NCLOB LOB locator of the target LOB.
src_lob BLOB, CLOB, NCLOB LOB locator of the rarget LOB.
Must be of the same type as
dest lob.
amount INTECER Number of bytes (BLOB) or
characters (CLOB, NCLOB)
to copy.
dest offset INTECER 1-based offset in bytes (BLOB)
or characters (CLOB, NCLOB)
into the destination LOB
indicating where the copied
data should go.
src offset INTECER 1-based offset in bytes (BLOB)
or characters (CLOB, NCLOB)
into the source LOB indicating
the start of the data to be copied.

lf src-offsetor dest_offset is < 1 or > LOBMAXSIzE, or if amountis


< 1 or > LOBMAXSIZE, the INVALID_ARCVAL exception is raised. The
following example illustrates the use of Copy. Because the amount is
greater than the length of v_Lol:.2, spaces are inserted.

-- Available online as lobcopy.sql


DECLARE
v_Lob1 CLOB;
v Lob2 CLOB;
832 OracteS pUSeL programming

tsEGTN
-- Retrieve source fob locator.
SELECT cfob cof
INTO v_Lob1
FROM lobdemo
WHERE key = 1-

-- Retrieve destination fob focator.


SELECT clob_cof
fNTO v_Lob2
FROM fobdemo
I^'HERE key = 2.
-- copy arr 26 characters from v Lobl
-- startingr at offset 50. Since v_.ob20nto the end of v_Lob2,
isn,t
-- long, spaces will be inserted. 50 characters
DBMS_LOB.COpy(v_Lob2, v_Lob1,
26, SO, tl;
-- And output the result.
LOBprinr (v_Lob2 ) ;
END;

NOTE
Th,il gxample uses the LOBprint
procedure,
which is defined later in this seciion.
tt'ur.,
DBMS_LOB.READ and DBMS_OUT\Ui
to
echo a character LOB to the sireen.

ERASE
You can use this function to erase
either an entire LoB or a poftion
rhe portion of the LoB that i' u,."r"J i, of it.
frii"i;i; ;#; ttf8u, NCL.B)
N?,:1h". the length of the LCjg remains
the same. To
e LOB value, you can use the TRIM
procedure.

PROCEDURE ERASE (
lob_loc tN OUT BLOB,
amount lN OUT tNTECER,
offsef lN INTECER:= 1);
Chapter 2l: Large Obiects 833

PROCEDURE ERASE (

lob_loc lN OUT CIOB CHARACTER SET ANY_CS,


amount lN OUT INTECER,
offset lN INTECER := 1);

The parameters for ERASE are described in the following table:

Parameter Datatype Description


lob_loc BLOB, CLOB, NCLOB Locator for the LOB to be erased.
amount INTECER Number of bytes (BLOB) or
characters (CLOB, NCLOB)to be
erased. lf offset+ amountis
greater than the length of the
LOB value, then the LOB is
erased only until its maximum
length. The actual number of
bytes or characters erased is
returned in amount.
offset INTECER 1-based offset into the LOB value
at which to start erasing.

FILECLOSE
This routine is used to close a BFILE that has been opened using FILEOPEN.
The syntax is

PROCEDURE FILECLOSE(f|le_loc lN OLJT BFILE);

where file loc is the locator for the BFILE to be closed. The FileExec
procedure in the section "LOADFROMFILE" later in this chapter
denronstrates Fl LECLOSE.

FILECLOSEALL
This routine is used to closb all BFILEs currently open in the session. lt is
useful in an exception handler, to ensure that the files are closed before
returning the error (similar to UTL_FILE.FCLOSEALL). lt is defined with

PROCEDU RE FI LECLOSEALL;
834 OracleS PUSQL Programming

and takes no arguments. The UNOPENED_FILE exception is raised if no


BFILE is currently open.

FILEEXISTS
This function is used to see if the specified file actually exists and is
readable on the file system. FILEEXISTS is defined with

FUNCTION FILEEXISTS (file_loc lN BFTLE)


RETURN INTECER;
PRACMA RESTRICT_REFERENCES(fi leexists,
WNDS, RNDS, WNPS, RNPS);

where file-loc is the locator for the desired BFILE. lf the file exists and is
readable, FILEEXISTS returns 1, otherwise it returns 0. A NULL is returned if
file_loc is NULL, does not have the necessary directory and operating
system privileges, or if there is an operating system error.

FILEGETNAME
Civen a BFILE locator, the FILECETNAME procedure will return the
directory and filename associated with the locator when it was initialized
(with BFILENAME). lt is defined with

PROCEDU RE FI LECETNAME(
file_loc lN BFILE,
di r_al i as OUT VARCHAR2,
fi lename OUT VARCUAR2);

where file-loc is the BFILE locator, and dir-alias and filename will contain
the directory and filename, respectively. The maximum length for dir_alias
is 30, and the maximum length for filename is 2000.

FILEISOPEN
This function is used to determine whether a given BFILE has been opened
(with FILEOPEN) for the given locator. lt is defined with
Chapter 2l: Large Objects 835

FU NCTION FILEISOPEN (fi le_loc I N BFTLE)


RETURN INTECER;
PRACMA RESTRICT_REFERENCES(fi leisopen,
WNDS, RNDS, WNPS, RNPS);

where file_loc is the BFILE locator for the desired file. lf the file has been
opened with this locator, the function returns 1, otherwise it returns 0.
FILEISOPEN just checks the status of the given locator passed to iu if it
returns 0 another locator could have the same external file open.

FILEOPEN
This function is used to open a BFILE for processing. rt is defined with

PROCEDURE FILEOPEN(
file_loc lN OUT BFILE,
open_mode lN BINARY_INTECER := FtLE_READONLy);

where file_loc is the locator for the desired file. The open_mode is used to
indicate the desired mode for the BFILE, sirlilar to UTL FILE. As of oracleB
Release 8.0, however, the only avail.rble nrode is read-only. This is
indicated by passing the constant DBNIS_LOB.FILE_READONLy for
open-mode. Future versions of or.rcle8 rvill likely include additional
modes and additional constants in DBNIS_LOB for specifying them.
INVALID_ARCVAL is raised if you pass a value other than
DBMS_LOB. Fl LE_READON LY for open_mode.
The FileExec procedure in the section "LoADFROMFTLE', rater in this
chapter demonstrates Fl LEOPEN.

GETLENGTH

overloaded to accept all four types of LOBs with


835 OracleS PUSQL Programming

FUNCTION CETLENCTH(\ob /oc lN BLOB)


RETURN INTECER;
PRACMA RESTRICT REFERENCES(getlength,
WNDS, RNDS, WNPS, RNPS);

FUNCTION CETLENCTH(lob /oc lN CIOB CHARACTER SET ANY CS)


RETURN INTECER;
PRACMA RESTRICT REFERENCES(getlength,
WNDS, RNDS, WNPS, RNPS);

FUNCTION CETLENCTH(file loc lN BFILE)


RETURN INTECER;
PRACMA RESTRICT REFERENCES(getlength,
WNDS, RNDS, WNPS, RNPS);

Here, lob_loc or file_loc specifies the LOB locator whose length is to be


returned. A NULL is returned if any of the following conditions is TRUE:

I The input LOB is NULL


I file_loc is not a valid open file
I file loc doesn't have the necessary directory and operating
system privileges

I file loc cannot be read because of an operating system error

Note that an exception is not raised in these cases.


The FileExec procedure in the section "LOADFROMFILE" later in this
chapter dernonstrates CETLE NCTH.

INSTR
The DBMS LOB.INSTR function is similar to the INSTR function defined in
package STANDARD for CHAR and VARCHAR2 character strings. lt is
used to find the position of the nth occurrence of a pattern in a given LOB.
INSTR is overloaded to accept all four types of LOBs:
Chapter 2l: Large Obiects 837

FUNCTION INSTR(
lob_loc lN BLOB,
pattern lN RAW,
offset lN INTECER := 1,
nth lN INTECER := 1);
PRACMA RESTRICT REFERENCES(instr,
WNDS, RNDS, WNPS, RNPS);

FUNCTION INSTR(
lob loc lN BFILE,
pattern lN RAW,
offset lN INTECER := 1,
nfh lN INTECER := 'l);
PRACMA RESTRICT-REFERENCES(i nstr,
WNDS, RNDS, WNPS, RNPS);

FUNCTION INSTR(
lob loc lN CLOB CHARACTER SET ANY-CS,
pattern lN VARCHAR2 CHARACTER SET lob-loc%CHARSET,
offset lN INTECER := 1,
nth lN INTECER := 1);
PRACMA RESTRICT-REFERENCES(i nstr,
WNDS, RNDS, WNPS, RNPS);

The parameters and return value for INSTR are described in the
following table:

Parameter DatatyPe DescriPtion


lob-loc, file-loc BLOB, BFILE, CLOB, Locator for the LOB to
NCLOB be examined.
pattern VARCHAR2, RAW String of bytes (BLOB, BFILE)
or characters (CLOB,
NCLOB) for which to
search. For NCLOBs, pattern
must have the same
character ser as lob loc.
838 Oracle8 PUSQL programming

Parameter Datatype Description


offset tNirce n 'l-based
offset in bytes
(BLOB, BFILE) or characters
(CLOB, NCLOB) into
lob_loc or file loc at
which the pattern matching
should start.
nth INTECER Occurrence number to
return, starting with 1.
return value INTECER Position in bytes (BLOB,
BFILE) or characters (CLOB,
NCLOB)within the LOB
where the nfh occurrence of
pattern starts, or 0 if it
cannot be found.

The FileExec procedure in the next section


.
demonstrates
',LOADFROMFTLE',
INSTR.

LOADFROMFILE

PROCEDU RE LOADFROMFILE(
dest_lob lN OUT BLOB,
src_lob lN BFtLE,
amount lN INTECER,
dest_offset lN INTECER := 1;
src_offset lN INTECER := 1);

PROCEDU RE LOADFROMFI LE(


dest_lob lN OUT NCTOB CHARACTER SET ANY_CS,
src_lob lN BFILE,
amount lN INTECER,
dest_offset lN INTECER:= 1;
src_offset lN INTECER:= j);
Chapter 2l: Large Objects 839

The parameters are described in the following table:

Parameter Datatype Description


dest_lob BLOB, CLOB, NCLOB LOB into which to write the data.
src lob BFILE File which should be read.
src_lob must have already been
opened and initialized (using
FILEOPEN) prior to calling
LOADFROMFILE.
amount INTEGER Number of bytes to read from
the source file.
dest offset INTEGER Offset (in bytes for BLOBs and
characters for CLOBS and
NCBLOBS) starting from 1 into
dest lob to start the write.
src offset INTECER Offset (in bytes, starting from 1)
into the source file to start
the read.

There are several things to note about LOADFROMFILE:

I lf dest_offset is beyond the current length of dest_lob, then null


bytes or spaces are inserted to fill the gap, si,'nilar to COPY.

I The destination LOB must be locked prior to calling


LOADFROMFILE. This implies that you have to SELECT a lob
from a table FOR UPDATE. Furthermore, the destination LOB
cannot be NULL.
I lf you are loading a CLOB, the operating system file should be in
the same character set as the database. No checking is done to
verify this.

The FileExec procedure illustrates LOADFROMFILE, along with several


other BFILE manipulation functions. lt can be used to read in and execute a
file of SQL statements.

-- Available online as fiLeexec.sql


CREATE OR REPLACE PROCEDURE FifeExec (
840 OracleS pUSeL programming

-- Executes the SeL statements in the fife identified


by
-- p_Directory and p_FileName. Each statement shoufd
not
-- contain the traiting. semicolon (unless it is a pllSeL
-- block) and shoufd be separated by p_Separat.ionChar.
p_Directory IN VARCHAR2,
p_FileName fN VARCHAR2,
p_SeparationChar fN CHAR) AS

v_Fifelocator BFfLE;
v_CLOBLocator CLOB;
v_SQLCursor INTEGER;
v_Start.Point INTEGER := 1;
v_EndPoint INTEGER;
v_SQlStatemenr VARCLAR2 (32000) ;
v_Statement.Length TNTEGER;
V-RC INTEGER;
BEGfN
-- Initialize the characLer focator for writing. Note that we
-- have to sefect a CLOB from a table FOR UPDATE. This
locks
-- t.he row; and is a requirement for LOADFROMFILE.
SELECT clob col
TNTO v_CLOBLocator
FROM lobdemo
WHERE key = -1
FOR UPDATE;

-- Initialize the BFILE l_ocator for reading.


v_FileLocat.or := BFfLEIIAME (p_Directory, p_Fi1eName)
DBMS-LOB . FILEoPEN ( v_Fi lelocator,
;
DBMS-LoB . FIIJE-RTjA.DoNLY ),

-- Set up the cursor.


v_SQlcursor := DBMS_SeL.OPEN_CURSOR;

-* Load the entire file into the character LOB.


-- This i-s necessary so that we have the data in
-- character rather than RAW variables.
DBMS_IJOB . IJOIDFROMF f LE ( v_CLOBLoc
at or, v_F i I e Loc at or,
DBMS LOB.GETLENGTH (v_Filelocator)
),
-- Loop over the LoB, searchi-ngr for each occurrence
of
-- the separation character,
LOOP
v_EndPoint, : = DBMS_LOB. INSTR(v_CLOBIJocator,
p_SeparationChar,
v Startpoint, 1),
Chapter 2l: Large Objects 84 I

EXIT WHEN v-EndPoint = 0;

-- Extract the contents between the starting and endj-ng


-- points. fhis is the SQL statement to be executed.
v_statementlength := v_EndPoint - v_StartPoj-nt;
v_SQlstatenent : = DB!,IS_LOB. SITBSTR (v_CLOBLocator,
v_Statementlength, v_StartPoint ) t

-- Echo the statement to the screen, and then execute it


-- using DBMS_SQL.
DBMS_OUTPUT . PUT_LINE (V_SQLStaternent ) ;
DBMS_SQL . PARSE (v_SQlCursor, V_SQLStatement , DBMS_SQL . V7 ) ;
v_RC := DBMS_SQL.EXECUTE (v_SQlcursor) t

-- Increment the statement poj.nter for the next statement.


v StartPoint := v EndPoint + 1;
END LOOP;

-- Clean up.
DBMS-LOB. FILECLOSE (v-FiIelocauor) i
DBMS_SQL. CLOSE_CURSOR (v_SQLCursor ) i
EXCEPTTON
WHEN OTHERS THEN
-- Cfose the cursor and fil-e, and reraise.
FILECLOSE ( v-Fi leLocator ) t
DBITIS_LOB .
DBMS_SQL . CLOSE_CURSOR ( v_SQLCursor ) ;
DATCF.
END FiLeExec;

Assuming that you have created a directory statements which contains the
following file:
-- Availa.ble online as inserts.sql
INSERT INTO temp_table (num_co], char_co1)
VALUES (1, 'hello')

INSERT INTO temp_table (num_col, char_col)


VALUES (2, 'he11o')

INSERT INTO temp_table (num_col, char_col)


VALUES (3, 'he]1o')

INSERT fNTO temp_tab1e (num_col, char_co1)


VALUES (4, 'he11o')
842 OracleS pUSeL programming

TNSERT INTO temp_table (num_col, char_col)


VALUES (5, 'he]1o')
INSERT TNTO temp_table (num_col, char
col)
VALUES (6, 'hell-o' )

INSERT INTO temp_tabJ-e (num_col, char_cof


)
VALUES (7, 'hel1o')

we can call FileExec to insert 7 rows into temp_table


with the following
PL/SQL block:

BEGTN
Fj-IeExec(,STATEMENTS,,,i,nserts.sqt,,,
/, ) ;
END,.

READ
READ is used to return a portion a given offset. lf
du
the end of the l_OB is reached
s returned as zero,
and NO DATA_FOUND is raised to accept all four
LOB types with:

PROCEDURE READ(/ob /oc tN B[OB,


amount tN OUT BtNARy_tNTECER,
offset lN |NTECER,
buffer OUT RAW);

PROCEDURE READ(/ob /oc rN croB CHARACTER sET ANy_cs,


amounttN OUT BtNARy_tNTECER,
offser tN il TECER,
buffer OUT VARCHAR2);

PROCEDURE READ(/ob toc tN BF|IE,


amount tN OUT BtNARy_tNTECER,
offsef tN tNTEGER,
buffer OUT RAW);

The parameters are described in the following table.


Chapter 2l: Large Objects 843

Datatype
Parameter Description
lob_loc BLOB, CLOB, LOB from which to read the data.
NCLOB, BFILE
amount BINARY INTECER Number of bytes (BLOB, BFILE) or
characters (CLOB, NCLOB)to read.
The actual number of bytes or
characters read is returned.
offset INTECER Offset in bytes (BLOB, BFILE)or
characters (CLOB, NCLOB) into
lob_loc to start the read, starting at 1.
buffer VARCHAR2, RAW Output buffer to receive the data.

The LOBPrint procedure demonstrates how to use READ to scan an


entire LOB. lt will echo the contents of a CLOB to the screen using
DBMS_OUTPUT, with B0 characters per line.

-- AvailabLe online as lobprint.sql


CREATE OR REPLACE PROCEDURE LOBPTinI(p-CLOB IN CLOB) AS
v_Buffer VARCHAR2 ( 80 ) ;
v_Offset INTEGER := 1;
v_Amount INTEGER := 80;
BEGTN
LOOP
-- output the rrext B0 characters.
Read and
(p_CLOB, v_Anount, v_Offset, v_Buf fer)
DBMS_IJOB.REAI) ;
DBMS OUTPUT.PUT LfNE(v Buffer);

v_Offset := v_Offset + v_Amount;


END LOOP;
EXCEPTION
WHEN NO DATA FOUND THEN
-- End of loop, just return.
NULL;
END LOBPrint;

SUBSTR
DBMS_LOB.SUBSTR beh.rves jrrst like tlre SUBSTR defined in package
STANDARD. lt is Lrsedto r€.tLrnr.r portion of .r LC)8, sinrilarto READ.
SUBSTR is overloaded to.rccept.rllfour kinds of LOBs lvith:
844 OracteS pUSeL programming

FUNCTION SUBSTR(/ob /oc tN BLOB,


amount lN INTECER := 32767,
offset lN INTECER := 1)
RETURN RAW;
PRACMA RESTRTCT REFERENCES(substr,
WNDS, WNPS, RNDS, RNPS);

FUNCTION SUBSTR(/ob /oc tN CLOB


CHARACTER SET ANy _cs,
amount lN INTECER := 32767
,
offset lN INTECER := .l)
RETURN VARCHAR2;
P.-RA_Cl4A R ESTR I CT_R EF E RE NC ES ( s u bstr,
WNDS, WNPS, RNDS, RNPS);

FUNCTION SUBSTR(/ob /oc tN BF|LE,


amount lN INTECER := 32767,
o/fser lN INTECER := 1)
RETURN RAW;
PRACMA RESTRTCT _REFERENCES(substr,
WNDS, WNPS, RNDS, RNPS);

The parameters are described below.

Parameter Datatype Description


lob loc BLOB, CLOB, LOB from which to read
NCLOB, BFILE the data.
amount INTECER Number of bytes (BLOB, BFILE)
or characters (CLOB, NCLOB)
to read.
offset TNTECER Offset in bytes (BLOB, BFTLE)
or characters (CLOB, NCLOB)
inlo lob_loc to start the read,
starting at 1.
return value VARCHAR2, RAW Selected portion of the LOB.

The FileExec procedure in the section


'LOADFROMFILE,, earlier in this
chapter demonstrates SU BSTR.
Chapter 2l: Large Objects 84S

TRIM
TRIM is used to remove data from the end of a LoB. Unlike ERASE, which
replaces portions of a LoB with spaces or NULLs, TRIM deletes the data,
and shortens the internal length. rt is overloaded to accept both BLoBs
and CLOBs with

PROCEDURE TRtM(/ob /oc tN CLOB,


newlen lN INTECER);

PROCEDURE TRIM(/ob /oc tN BLOB,


newlen lN TNTEGER);

where lob-loc is a locator for the LoB to be trimmed , and newlen is the
new length of the LOB. Any data past newlen will be deleted. lf newlen is
greater than the size of the LoB, the error , oRA-22g26: specified trim
length is greater than current LoB value's length" is raised. lf lob_locis
NULL, then VALUE-ERROR is raised. However, if you cail TRIM on an
empty LoB, no error is raised and the LoB is unchanged. The following
example illustrates the behavior of TRIM.

-- Availabl.e online ae lobtrim.sql


DECLARE
v_BLOB BLOB;
BEGfN
SELECT blob co1
INTO v BLOB
FROM lobdemo
WHERE keY = 1
FOR UPDATE;

DBMS_OUTPUT.PUT_LINE(,Before trim, length =,


I I

LoB' GETLENGTH (v BLoB) ) ;


DBr4s_rJoB.TRrM(v_Br,oB, 3iyt
DBMS_OUTPUT.PUT_LINE('After trim, length = , ll
DBMS LOB.GETLENGTH (v_BLOB) ) ;

-- Raises ORA-22926, since the lengt.h is now 5.


DBMS LOB.TRIM(V_BIJOB, LOl ;
E}trD;
846 OracteS pUSeL programming

WRITE
DBMS-LoB.wRrrE is used to write data into a
section of a LoB. rt will
overwrite any existing aa11i1 the range specified.
rf the offseiis beyond the
lgls,h of the destination LoB, then ,p"".", or zerobytes are inserted as
filler. WRIrE is overloaded to accept both cloBs
and NCLoBs with
PROCEDURE WRTTE(/ob_loc tN OUT BLOB,
amiunttN Bt NARY_INTECER,
o/fset lN |NTECER,
buffer lN RAW);

PROCEDURE WRTTE(/ob_toc tN OUT CLOB


CHARACTER SET ANY_CS,
amounttN Bt NARy_tNTECER,
offset lN tNTECER,
buffer lN VARCHAR2);

The parameters for wRrrE are described in the


foilowing tabre.
Parameter Datatype Description
lob_loc BLOB, CLOB, NCLOB LOB into which the data should
be written.
amaunt BTNAR'-TNTECER Number of characters (cLoB,
NCLOB) or bytes (BLOB) to
write. amount should be <= the
length of buffer.
offset INTECER Offset in bytes (BLOB)or
characters (CLOB< NCLOB)
starting from .l into lob_loc.
buffer RAW, VARCHAR2 Data to be written into the
destination LOB.
Chapter 2l: Large Objects 847

Exception Error Code Error Message


ACCESS_ERROR 22925 Operation would exceed
maximum size allowed for a LOB
INVALID_ARGVAL 21560 Argument is NULL, invalid, or out
of range
22287
INVALID_DIRECTORY lnvalicl or modified directory
NO DATA FOUND*1403 No data found
22285
NOEXIST_DIRECTORY Directory does not exist
22286
NOPRIV_DIRECTORY lnsufficient privileges on directory
OPEN TOOMANY 22290 Maximum limit reached on
number of open files
22288
OPERATION_FAILED File operation failed
UNOPENED_FILE 22289 Cannot perform operation on
unopened file
VALUE ERROR* 6502 Numeric or value error

TABLE 2l-2. Exceptions Raised by the DBMS LOB BFILE Routines

* This exception is defined in package STANDARD, rather than DBMS_LOB.

Exceptions Raised by
DBMS LOB Routines
Table 27-2 descibes the exceptions that can be raised by the DBMS-LOB
routines. For more information, see the descriptions of the individual routines
in the previous sections. Except where noted, all these exceptions are defined
in DBMS LOB.

DBMS LOB vs. OCI


Besides the DBMS_LOB package, LOB values can be manipulated using
the Oracle8 OCl. A full discussion of the OCI LOB functions is beyond the
scope of the book. For more information see the Programmer's Guide to the
848 Oracle8 PUSQL Programming

DBMS LOB
Equivalent
OCI Function Function Description
OClLobAppend0 APPEND Appends the contents of one
LOB value to another LOB
OClLobAssign0 N/A Assigns one LOB locator to
another
OClLobCharSetForm0 N/A Returns the character set form
for a given LOB
OClLobCharSetld0 N/A Returns the character set lD for
a given LOB
OClLobCopy0 COPY Copies a portion of a source
LOB into a destination LOB
OClLobErase0 ERASE Erases part of a LOB from a
specified offset
OClLobGetlength0 CETLENCTH Returns the length of a LOB or
BFILE
OClLoblsEqual0 N/A Checks if two LOB locators
point to the same LOB value
OCILobLocatorlslnit0 N/A Checks if a given LOB locator is
initialized
OClLobLocatorSize0 N/A Returns the size of a LOB locator
OClLobRead0 READ Reads a specified portion of a
source LOB or BFILE into a
supplied buffer
OClLobTrim0 TRIM Truncates a LOB
OClLobwriteO WRITE Writes data from a buffer into a
LOB starting at a given offset,
overwriting existing data
OClLobFileOpen0 FILEOPEN Opens a BFILE
OClLobFilelsOpen0 FILEISOPEN Returns TRUE if a BFILE is open

TABLE 2l-3. oCI LoB Manipulation Functions


Chapter 2l: Large Objects 849

DBMS LOB
Equivalent
OCI Function Function Description
OClLobFileExists0 FI LEEXI STS Checks whether a BFILE exists
OClLobFileCloseO FILECLOSE Closes a BFILE
OClLobFileCloseAll0 FILECLOSEALL Closes all BFILEs currently open
OClLobFileSetNameO N/A Sets the name of a BFILE
in a locator
OClLobFilecetName0 FILECETNAME Returns the name of a BFILE
N/A COMPARE Compares portions of two
LOB values
N/A INSTR Used to pattern match within
a character LOB
N/A SUBSTR Returns a specified portion of a
LOB value
N/A LOADFROMFILE Copies a BFILE into a CLOB
or BLOB

TABLE 2l-3. OCI LOB Manipulation Functions 6ontinued/

oracle9 Call lnterface, volume 1: oCl Concepts. For reference, howeVer,


Table 21-3 lists the DBMS LoB functions and their oCl equivalents. Nore
that there are several functions available only in oCl and several available
only in DBMS_LOB.

PUSQL at Work:
Copying LONG Data into a LOB
As we have seen in this chapter, LoBs offer many advantages over oracleT
LONG and LONC RAW types. As a result of these, you may want to
convert existing data in LONC columns to an equivalent LOB. The
Long2lob procedure is designed to do exactly that, entirely in pLlSeL.
850 Oracle8 PUSQL programming

Long2lob uses DBMS_seL to fetch the LONG data piecewise, and then
DBMS_LOB ro insert it piecewise into rhe LOB. (See Chapter 15
for
information on DBMS_seL.)The procedure is defined as follows:

-- Available online as 1ong21ob.sql


CREATE OR REPLACE PROCEDURE Long2lob(
-- Uses DBMS_SeL to sefect a LONG cofumn identified by
-- p_Longeuery, and returns j-t i-n p_Cl,ob.
p_Long'euery rN VARCHAR2,
p-Cl-ob IN oUT CLOB) AS

c_ChunkSize CONSTANT fNTEGER := 100;


v_CursorlD INTEGER;
v-RC INTEGER;
v_Chunk VARCHAR2 ( 100 ) ;
v_Chunklengrth INTEGER;
v_Offset TNTEGER := 0;
BEGIN
-- Open the cursor, define, execute, and fetch.
v_CursorID := DBMS_SeL.OpEN_CURSOR;
DBMS_SQL . PARSE ( v_CursorfD, p_Lonqeuery, DBMS_SQL
. V7 )
DBMS_SQL. DEFINE_COLUMN_LONG (v_CursorID, 1
);
v_RC : = DBMS_SeL. EXECUTE_AND_FETCH (v_CursorfD) ;

-- Loop over t.he LONG, fetching c-chunksize characters at


-- t-ime from the LONG and adding them to the LOB.
LOOP
DBMS_SQL.COLLIMN_VALUE_LONG (v_CursorfD, 1, c_ChunkSize,
v_Offset, v_Chunk, v_Chunklength) ;
DBMS_LOB.WRfTE(p_Clob, v_Chunklength, v_Offset + 1,
v_Chunk) ;
TF v_Chunklength < c_ChunkSize THEN
EXlT;
ELSE
v_Offset := v_Offset + v_Chunklength;
END fF;
END LOOP;

DBMS_SQL . CLOSE_CURSOR ( v_Curs orrD ) ;


Chapter 2l: Large Objects 85 |

EXCEPTION
WHEN OTHERS THEN
-- Clean up, and reraise the error.
DBMS-SQL. CLOSE-CURSOR ( v-CursorID ) ;
RAISE;
END Long2lob;

Suppose we have a table containing a LONC, created ancl populated with:

-- Availabie online as part of l21test.sql


CREATE TABLE long_tab (

key NUMBER,
longr_col LONG
);
INSERT INTO fong-tab (key, long-col)
VALUES (100,
'abcdef ghi jkfmnopqrstuvwxyZABCDEFGHIJKL}INOPQRSTlt\'/WZYZabcdef qihi jklmn
opqrstu\ vxyzABCDEFGHIJKLMNOPQRSTLIVWZYZabcdefghiiklmrropqrstu\,^r'/xyzABC
DEFGHIJKLMNOPQRSTLIWZYZabcde fghij klmnopqrs tu\/wT'yzABCDEFGHIJKLMNOPQR
STtryWZYZabcdefghij klmnopqrstu\TwxyzABCDEFGHI.IKLMNOPQRSTUVU/ZYZabcde fg
hi j klmnopqrstu\ vxyzABCDEFGHIJKLMNOPQRSTUWZYZabcdef ghi j klmnopqrs tuv
wxyZABCDEFGHI.TKLMNOPQRSTUI/WZYZabcdefghi j klmnopqrstuvwxyzABCDEFGHIJK
LMNOPQRSTUVWZYZabcdef ghi j klmnopqrstuv\^/xyzABCDEFGHI'-IKLMNOPQRSTU\JWZYZ
abcdef ghi j klmnopqrstur,'wxyzABCDEFGHI,fKLMNOPQRSTUWZYZabcdef ghi j klmno
pqrs tu!'wxyzABCDEFGHIJKLMNOPQRST'Ln4\iZYZabcdef ghi j klnnopqrs tuvwxyzABCD
EFGHI JKLMNO PQRSTWWZYZ abc de f gh i j klmnopqr s tuvwxyzABCDEFGHI JKLMNOPQRS
TrJWIZYZabcdefqhi j klmnopqrs tu\/wxyzABCDEFGHIJKLMNOPQRSTII-JWZYZabcde fgh
i j klnnopqrs tuvwx\.zABCDEFGHIJKLMNOPQRSTWWZYZabcde fghi j klmnopqrs tuvwxy
zABCDEFGHI.IKLMNOPQRSTUVWZYZabcdefqhij klmnopqrstuvuD.yzABCDEFGHIJKLMNOP
QRSTL'VWZYZabcdefqhijklmnopqrstuvwxlzzABcDEFGHIJKI,IOJOPQRSTTJVINZYZ');

We can move the data from long-col into lobdemo by calling Long2lob
with the following:

-- Available online aa part of l2ltest.sql


DECLARE
v_Query VARCHAR2 (200) ;
v_NewLOB CLOB;
BEGIN
v-Query := 'SELECT long-col FROM fong-tab where key = 169''
INSERT INTO fobdemo (key, clob-col)
852 OracleS PUSQL programming

VALUES (100, EMPTY-CLOBO )


RETURNfNG clob_col INTO v NewLOB;
Long2lob (v_euery, v_NewLOB) ;
LoBprint (v_NewLOB) ;
COMM]T;
END;

There are several things to note about the usage of long2lob:

r p-LongQuery must be a sELECT statement which matches


1 row
and returns 1 column, which is the LONC.
I lf you select an existing LoB lcicator from a table, rather
than
inserting a new one, be sure to SELECT FoR upDATE
to rock the
row, since Long2lob will update it.
I The technique used on Long2lob can be adapted to
convert an
entire table, by using DBMS_SeL to serect the LoNC
column from
every row, and then looping over each returned row to write
the
data into the LOB.

Summaty
ln this chapter, we have examined the oracle8 implementation
of LoBs
(large objects). The four different types
of LoBs-CLoBs, NCLoBs, BLoBs,
and BFILEs-are used to store,diffeient types of data, which
can be up to 4
gigabytes in length. Data can be stored internar to oracre,
anJ thus be
managed by the database, or it can be stored external to
oracle, and thus
ing system. We also examined the
SQL interface to manipulate LOB
e will look at improving the
ons.
856 OracleS PUSQL Programming

t is important that your application do what it is expected to


do. lt is equally important that it do the necessary
processing as fast and efficiently as possible. Developing a
properly tuned PLISQL applicarion is not difficulr, as long as
you keep in mind a few concepts. These are the shared
pool, properly designed sQL statements, and an understanding of network
issues. We willdiscuss all of these in this chapter and see how you can use
them in your own applications. We will also discuss the third and final
component of SQL-Station, the Plan Analyzer.

The Shared Pool


The shared pool is an area of memory in which oracle stores information
about SQL statements and stored subprograms. Setting the size of the
shared pool and using it properly are key components of a properly tuned
PL/SQL application.

Structure of an Oracle lnstance


ln order to discuss how the shared pool works and its implications for
PL/SQL programming, we first need to examine the structure of a running
oracle instance. An instance consists of processes, memory, and operating
system files, all running on the server. Additional client processes can
connect to the database as well. Figure 22-1 illustrates the structure of
an instance.

NOTE
On some systems (such as Windows 3.1) an
Oracle instance consists of a single process
only. This process is responsible for all
database administration, in addition to
serving as a shadow process. The shared
pool behaves the same, however.

Processes
When an instance is started, a number of Oracle background processes are
started. These processes communicate with each other via shared memory,
known asthe SCA (system global area). All of the Oracle processes can
Chapter 22: Performance and Tuning 857

Other
Oracle
Processes

User User
Process Process

FIGURE 22- 1. An oracle instance

read and write to various data structures in the SCA. Each process is in
charge of a different aspect of running the database. Some of these
processes are described in Table 22-1. A full discussion of how these
processes work is beyond the scope of this book; for more information, see
Oracle Server Concepts. Here, we will examine user and shadow processes.

USER PROCESSES A user process is an application that sends SQL


and PL/SQL to the server to be processed. These applications can be
developed using any of Oracle's development tools, such as the
precompilers, OCl, SQL*PIus, and the Developer 2000 suite, or a
third-party application development tool, such as SQl-Station. The user
process communicates with the shadow process over the network using
SQL*Net. This communication and its implications are discussed later in
this chapter, in the "Network lssues" section.

SHADOW PROCESSES The shadow process is responsible for passing


information back and forth between a user process and the rest of the
instance. Without the multithreaded server, there is one shadow process
858 Oracle8 PUSQL programming

Process Description
Database Writer (DBWR) Writes changed information from the buffer
cache in the SCA to the database files.
Process Monitor (PMON) Cleans up after failed user processes, such as
releasing locks and rolling back any
uncommitted SQL statements.
System Monitor (SMON) Performs instance recovery (if necessary) when
the instance is started. Also responsible for
freeing temporary segments and coalescing free
space in the SCA.
Recover (RECO) Resolves fai I ures with d istributed transactions.
RECO is only present when the Distributed
Option is used.
Log Writer (LCWR) Writes the redo log buffer to the redo log on disk.
LCWR and DBWR work together to ensure that
all committed information is properly written
before the commit is successful.
Archiver (ARCH) Copies full redo logs to an offline storage device,
such as a tape drive. ARCH is only running when
the database is in ARCHIVELOC mode.
Snapshot Refresh (SNp) Refreshes table snapshots and runs database
jobs (see Chapter .lB).
Shadow Process Manages the information transfer between the
user process and other database resources.
User Process Runs a user application, sending SeL
statements and PL/SQL blocks to the database.
Dispatcher Process Allows multiple user processes to share a single
shadow process, as part of the multithreaded
server. The user processes connect to the
dispatcher, which in turn passes information on
to the shadow process. The connections
between the user and dispatcher process must
use SQL*Net version 2.

TABLE 22- l. Database processes


Chapter 22: Performance and Tuning 859

per user process. With the multithreaded server, a single shadow process
can manage multiple user processes, with the help of ihe dispatcher
processes. For more information on the multithreaded server and how to
configure it, see Oracle Server Concepts.

Memory
The memory used by the instance is partitioned into different areas, which
are used by different processes. There are four different kinds of memory:
the shared pool, DB block cache, redo log buffer, and the process global
area (PCA). All but the PCA are allocated as part of the SCA and are thtrs
available to all processes. The SCA is completely allocated when the
instance starts up. lts size is determined by various INIT.ORA parameters,
i ncl ud i ng SHARE D_POOL_S lZE, LOC_B U FFER, and DB_B LOCK_B U FFERS.

For more information on these and other INIT.ORA parameters, see the
Orac I e Se rver Refe re nce.

SHARED POOL The shared pool is the memory structure that most
affects the performance of PL/SQL. The shared pool contains the text of
SQL statements and PL/SQL blocks sent to the database, along with the
parsed representation of them. ltems in the shared pool do not necessarily
remain there forever; they are aged out of the pool when they are no longer
necessary. We will see how this works and examine the shared pool in
detail shortly.

DB BLOCK CACHE Data is read from and written to database files


in blocks. For maximum efficiency, the size of data blocks is usually a
multiple of the operating system block size and is specified by the INIT.ORA
parameter DB_BLOCK_SIZE. Typical values for DB BLOCK SIZE are 2O4B
or 4096, since operating system blocks are often 1024 bytes. The shadow
processes read data blocks from the data files. These are stored in the DB
block cache and manipulated there. lf the contents of a block are changed,
DBWR will write the block back to the file.

REDO LOG BUFFER The redo log buffer behaves the same as the DB
block cache except that it contains redo blocks rather than data blocks.
Redo information is generated by SQL statements and PL/SQL blocks as
they are processed. This redo information is used in case of instance failure
860 OracleS PUSQL Programming

and for read-consistency. LCWR is responsible for writing the blocks irr the
redo log buffer to the online redo logs.

PGA Each shadow process allocates memory for its own use. This
memory is known as the process globalarea (pcA). lnformation in the pcA

is kept in the scA. This is necessary because a session can migrate between
shadow processes with MTS.

Files
The database uses three different kinds of operating systern files, each of
which stores different kinds of information. Different processes are in
charge of maintaining different files. The three kinds of files are database
files, redo log files, and control files. These files are different from external
files used b;rthe urL_FILE package (see Chapter 1B) and external files used
to store BFILEs (see Chapter 21) in Or-acleB.

DATABASE FILES These files srore the actual data found in the
database. This includes table and index data, code for stored subprograms,
view definitions, and sequence information, along with the data dictionary
itself. DBWR is responsible for writing to database files, and the shadow
processes can read from them.

REDO LOG FILES The redo log records all of the changes made to
database objects. This information is used to generate a read-consistent

coNTRoL FILES rhe current state of the database is stored in one or


more control files. lf there is more than one file, all of them are kept in the
same state (they are mirrorec!) to aid recovery in case one is lost. The
database state includes the number of data and redo files and how much
Chapter 22: Perforrnance and Tuning 85 I

information has been written to each. LCWR also modifies the control files,
and ARCH is responsible for recording these changes in the otTline storage.

How the Shared Pool Works


Whenever a SQL statement is received by the database, it is parsed and the
execution plan determined. This information is then stored in the shared
pool. lf the same SQL statement is received by the database at a later point
(issued by the same user process or a different user process), the database
does not have to reparse the statement. The parsed form is already there in
the shared pool. This performance enhancement can significantly reduce
the amount of work done by the server, especially in environments with a
large number of users running the same application'
When the database receives a SQL statement from a client application,
it first checks to see if the statement is in the shared pool. lf it is already
there, the parsed form and execution plan are immediately available. lf the
statement is not in the shared pool, itwill be placed there after it is parsed.
The important thing to note about this process is that the database has to
receive the statement before it can be compared. Even if the statement is
found in the shared pool, the statement does travel over the network each
time. Although the database may not have to do any work with the
statement once it gets it (since it's in the pool already), it does have to
receive the entire statement first. For long SQL statements, this can be
significant. Because a pointer to the parsed statement is returned to the
client the first time the statement is issued, the client application can be
written to avoid subsequent parse calls altogether. See the section
"Avoiding Unnecessary Reparsing" later in this chapter for more information.
Besides the parsed form of SQL statements, the shared pool also holds
the compiled p-code for stored subprograms and the contents of database
pipes. When a stored subprogram is called for the first time, the p-code is
retrieved from disk and kept in the shared pool. The next time the same
object is referenced, the disk access can be avoided since the p-code is
already in memory.
When the shared pool fills up, objects within it are aged out according
to an LRU (least recently used) algorithm. The object that hasn't been
accessed for the longest time is retnoved from the pool, and the space it
took up is used for a new object. Only those objects that are not currently
being used are eligible for aging. lf a subprogram is still being executed, for
exariple, it will ntlt be agedlui. Since ail otrlects are not the same sizer,
862 Oracle8 PUSQL Programming

this can result in shared pool fragmentation-the space for a new object
may be available, but it is not contiguous. When there is not enough
memory in the shared pool for a new object, the following Oracle error is
returned:

ORA-4031: unabfe to allocace X bytes of shared memory

This can happen either because the shared pool is not big enough, or the
space in it has become fragmented.

Flushing the Shared Pool


lf you receive the ORA-4031 error, one solution is to flush the shared pool.
This will remove all eligible objects (those that are not currently being
used). The command to do this is

ALTER SYSTEM FLUSH SHARED POOL;

You can issue this command from any account with the ALTER SYSTEM
system privilege. The shared pool can be flushed while the database is still
running without affecting current applications. This ALTER SYSTEM
command is not usually issued from an application; rather, it is issued from
a DBA session when necessary.
The shared pool is also flushed when the instance is brought down.
When an instance is brought up, the shared pool is empty because no SeL
has been executed yet.

Triggers and the Shared Pool


P-code is only cached if the subprogram is stored. Prior to OracleZ Release
7.3,Ihis does not includetriggers. ln releases earlierthan 2.3, the compiled
p-code for triggers is not stored in the data dictionary----only the source is.
Consequently, when a trigger is fired for the first time, it must be compiled.
The compiled code is then stored in the shared pool for subsequent trigger
firings. However, if the trigger gets aged out of the shared pool, it must be
recompiled before it can be executed again.
It is a good idea to keep your trigger code as small as possible, to
minimize the time spent compiling it. This can be accomplished by moving
the work done in a trigger into a packaged subprogram and then calling the
Chapter 22: Per-formance and Tuning 863

package from the trigger. The trigger thus consists only of the call to the
package, which is comparatively short.
Packaging your triggers is not necessary in OracleZ Release 2.3
because triggers are stored in compiled form with this release of
PLlsQL and the database. Calling packaged procedures from a trigger is
still a good idea, however. This way the code can be called from oiher
places as well.

The Shared Pool and the Multithreaded Server


when using the multithreaded server, some of the session information that
is normally kept in the PCA is moved to the shared pool in the scA. This is
necessary because a database session may be handled by different shadow
processes. lf a session is migrated to a different shadow, the new shadow
needs to have access to the session information. Thus it has to be accessible
to both processes in the shared pool. This implies that the shared pool
needs to be larger when using the multithreaded server. For more
information, see the Oracle Server Reference.

Estimating the Size of the Shared Pool


The size of the shared pool is specified by the INIT.ORA parameter
SHARED-PooL-slzE. The default value is 3.5M8. This amount of memory
is allocated when the instance is started, and it will not grow or shrink. lf
there is not enough contiguous memory available at startup time, the
database will not come up.
Since the shared pool is a fixed size, it is important to set the size
properly. The best way to do this is to examine the size of the objects you
use frequently, and make sure that they can all fit in the shared pool
simultaneously. lf not, then they will be aged in and out, resulting in a
fragmented pool and the ORA-4031 error.
There are various methods for sizing the shared pool properly. They will
not be discussed in full detail here-for more information see Ciracle 3"rr.,
Tuning. Essentially, you determine the size for each of the objects you
commonly access. Add these together, and you have the minimum shared
pool size. lf you are using the multithreaded server, you also need to
include the size of the session information.
864 OracteS pUSeL programming

records the size of stored pLlSeL


returns the size of several

SQL> SELECT name, t]4)e, code_size


2 dba_object_size
FROM
3 WHERE name IN (,DBMS_pfpE,, ,STANDARD,, ,DBiqS_OUTPUT,)
4 ORDER By name, tlpe;

NAME
TYPE CODE_SIZE
DBMS_OUTPUT
PACKAGE 388
DBMS-OUTPUT
PACKAGE BODY 62I7
DBMS-OUTPUT
SYNONYM 0
DBMS-PfPE
PACICAGE 699
DBMS-PTPE
PACKAGE BODY 6427
DBMS-PfPE
SYNON\'}4 0
STANDARD
PACKAGE I0494
STANDARD
PACKAGE BODY 22400
The code-size corumn in this view wiil
contain the size of the object.
You can query dba-object_size for ail
of the onj".,, y-o, commonry access.

Session Memory
The session memory usage for a particurar
database session can be
determined from the v$slsstat and vgstatnu."
you first need to identify a database
ui"*r. in ord",. to do this,
session identifier with a query such
as
SeL> SELECT sid
2 FROM v$process p, v$session s
3 WHERE p.addr = s.paddr
4 am s.username = ,SySTEM,;
SID

This query gives the session identifierr for


the session connectecr as the
oracle user sysrEM (you can specify a crifr,ererrt
Lrser as weil). Now that
Chapter 22: Performance and Tuning 865

we know the identifier, we can determine the session memory for this
session with

SQL> SELECT value


2 FROM v$sesstat s, vgstatname n
3 WHERE s.statistic# = n.statistic#
4 AND n.nalne = ,session uga memory max,
5 AND SID = 6;

VALUE

941 04

Thus the memory usage for this session so far is 94,7O4 bytes. This
query should be run for a session after it has been running for some time,
since it returns the maximum amount of memory used by a session so far,
not the maximum amount it will use. Multiply this by the number of
sessions to determine the necessary shared pool size.

Pinning Objects

and DBMS SHARTD POOL.SIZES.

KEEP
The DBMS-SHARED-POOL.KEEP procedure is used to pin objecrs in the
pool. Packages and sQL statements can be pinned. ln oracle8, sequences
and triggers can also be pinned. KEEP is defined with

PROCEDU RE KEEP(name VARCHAR2,


f/ag CHAR DEFAULT'P');
866 Oracle8 PUSQL Programming

The parameters are described in the following table. once an object has
been kept, it will not be removed until the database is shut down or the
DBMS_SHARED_POOL. U N KEEP procedure is used.

Parameter Type Description


name VARCHAR2 Name of the object. This can be a package
name or the identifier associated with a
SQL statement. The SQL identifier is the
concatenation of the address and
hash_value fields in the vgsqlarea view
and is returned by the SIZES procedure.
flag CHAR Determines the type of the object. lf flag is
'p' (the default), then name should match
a package name. lf flag is 'C' (for cursor)
than name should contain the text of the
SQL statement. lf 'S', then rrame should be
a sequence, and if 'R', then name should
be a trigger. The 'C', 'S', and 'R' values are
valid only in Oracle8.

UNKEEP
UNKEEP'is the only way to remove a kept object from the shared poor.
Kept objects are never aged out automatically. UNKEEp is defined with

PROCEDU RE U N KEEP(name VARCHAR2,


f/ag CHAR DEFAULT 'P');

The arguments are the same as for KEEP. lf the specified object does not
already exist in the shared pool, an error is raised.

stzEs
SIZES will echo the contents of the shared pool to the screen. lt is
defined with

PROCEDU RE S|ZES(minsrze N UMBER);


Chapter 22: Performance and Tuning g6T

objects with a size greater than minsize will be returned. sIZES uses
DBMS-OUrPUT to return the data, so be sure to use ,'set serveroutput on,l
in sQL*Plus or server Manager before calling the procedure. For more
informajion on DBMS_OUTPUT, see Chapter 13.

SQL Statement Tuning


ln order to execute a sQL statement, the database must determine the
exe.cution plan. An execution plan is the method by which the database
will actually process the statement-what tables and indexes it needs to
access, whether or not a sort operation needs to be done, and so on. The
execution plan can have a large impact on the length of time it takes to
execute a sQL statement. This section describes how to tune sel
statements in general, whether or not they are inside a pL/SeL block.

Determining the Execution plan


There are several different methods for determining the execution plan. The
EXPLAIN PLAN state-mext is a good method for querying the plan quickly
and easily. The TKPRoF u.tility will al: o give you the ex"ecution plan, along
with additional statistics about the seL plocessing. sel-station plan
Analyzer can also be used to determine the plan.

EXPLAIN PLAN
The EXPLAIN PLAN sQL statement will determine an execution plan for a
given statement and inseft it into another database table. The format for
EXPLAIN PLAN is

EXPLAIN PLAN [SET STATEMENT _lD = ,statement_info,]


[l NTO pl an_table] FOR sgl_sra temen1

where sql_statement is the statement you want to explain. The plan will be
inserted into plan_table.lf plan_table isn't specified, then it defaults to
PLAN_TABLE. This table can be created with:

CREATE TABLE plan--table (


statement_id VARCHAR2 (30) ,
t j.mestamp DATE,
858 OracteS pUSeL programming

remarks VARCHAR2 (BO) ,


operation VARCITAR2 (30),
options VARCHAR2 (30) ,
object_node VARCTAR2 (30) ,
object_owner VARCHAR2 (30) ,
object_name VARCHAR2 (30) ,
obj ect_instance NUMBER,
object_type VARCHAR2 (30) ,
search_cofumns NUMBER,
id NUMBER,
parent_id NUMBER,
position NUMBER,
other LoNG);

ln order to use EXPLATN PLAN, you shourd either


have pran_tabre in
your own schema, or yoy should specify another
plan tablJi*i,n tn"
preceding definition) in the EXPLniN pmN statement.
The stateme nt_id, if
rN PLAN, wit be inserted into the statement_id
corumn
is used to store multiple plans in the same table_the
key for each statement. rf the statement rD arready
exists
s plan is replaced.

For example, we can determine,an execution pran


for a query against
registered_students and classes vith

EXPLAIN PLAN
SET STATEMENT-ID = /QueTy 1, FoR
SELECT rs.course, rs.department, students.ID
FROM registered_students rs, students
WHERE rs.student_id = students.id
AND students.last_name =, Razmataz,
;

once we issue this statement, we can query the plan


table with the
fol lowing SeL statement:
Chapter 22- Performance and Tuning 859

SELECT LPAD(' ', 2 * (LEVEL - 1)) ll operation ll


ll options ll ll object_name l' ll
DECODE(id, 0, 'Cost = ' | | position) "Execution Plan"
FROM plan_table
START WITH id = 0
AND statement_id = ,euery 1,
CONNECT By pRIOR i6 = parent id
AND statement_id = ,euery 1,;

Notethatthe same statement lD is used both in the EXPLAIN PLAN


statement and the query of thb plan table. The previous query returns the
following output:
Execution Plan

SELECT STATEMENT Cost =


NESTED LOOPS
TABLE ACCESS FULL REGISTERED STUDENTS
TABLE ACCESS BY ROWID STUDENTS
INDEX UNTQUE SCAN SYS-COOB59

We will discuss how to interpret the execution plan later in this chapter,
in the section "Using the Plan."

TKPROF Utility
The EXPLAIN PLAN statement is useful for determining the execution
plan, as we saw in the previous section. The TKPROF utility, however,
can give us the execution plan as well as statistics about how well the
SQL statement actually performed. ln order to use TKPROF, first you need
to get a SQL trace file for your session. TKPROF is then used to format the
trace file and make it readable. ln order to generate a trace file, issue the
SQL command

ALTER SESSION SET SQL-TRACE = TRUE;

before any SQL statements you want to examine. This will start a trace file,
which will contain information about any subsequent SQL statements or
PL/SQL blocks submitted to the database. lnformation will be dumped to
the trace file until the session ends or tracing is turned off with

ALTER SESSION SET SQL-TRACE = FALSE;


870 Oracle8 PUSQL Programming

The location of the trace file is determined by the USER_DUMP_DEST


parameter in |N|T.ORA. The name of the trace file is system specific but
will usually start with "ora" and contain the process identifier of the shadow
process. For example, a trace filename could be "ora_l2345.trc". The
easiest way to determine the correct trace file is to look in
TJSER_DUMP_DEST for the newest file immediately after issuing the trace.

CAUTION
ALTER SESS/ON is not allowed in a PUSQL
block, since it is not a DML statement. You
can issue the ALTER SESS/ON before issuing
the block, however. Alternatively, you can
trace SQL statements outside of a block.

For example, we can issue the following statements from SQL*Plus:

SQL> ALTER SESSION SET SQL_TRACE = TRUE;


Session altered.
SQL> SELECT rs.course, rs.department, students.ID
FROM registered_students rs, students
WHERE rs. student id = students. id
AND students.]ast_name ='Razmataz' ;

COURSE DEP ID

101 Hrs 10010


307 NUT 10010
SQL> ALTER SESSION SET SQL-TRACE = FALSE;
Sessi-on altered.
. This produced a file "ora_29338.trc" in USER_DUMP_DEST. We now
need to format this trace file to produce a readable output. We use TKPROF to
do this. TKPROF is a utility that is run from the operating system. lts location is
system dependent, but it is usually found in the same directory as other Oracle
executables such as SQL*Plus itself. The format for TKPROF is

TKPROF input_file output_file ISORT = soft_options] [PRINT = numJtrint\


IEXPLAIN = userl passwordl

where input_file is the name of the generated trace file (ora_29338.trc in


this case), and output_filewill contain the formatted trace file. lf SORT is
Chapter 22: Performance and Tuning 871

not specified, the SQL statements will appear in the order they were
submitted. Otherwise, you can sort based on one or more of the options
listed in Table 22-2. To specify more than one sort option, use the syntax

SORT - (optionl, option2, ...)

To include an execution plan in the trace file, specify a user name and
password in the EXPLAIN option. TKPROF will create a plan table, run
EXPLAIN PLAN into this table, select the output into the file, and drop the
table. lf specified, only num_printstatements will be included in the file,
after sorting.
We can format our trace file with

TKPROF ora 29338.trc trace.out EXPLAIN=example/example

which produces an output file "trace.out". A portion of this file follows:


SELECT rs.course, rs.department, students.ID
FROM registered students rs, students
WHERE rs. student id = students. id
AND students.last name = 'Razmataz'

cal-l count cpu elapsed dj-sk query current rows

Parse 1 0.00 0.00 0 0 0 0


Execute 1 0.00'O.OO O 0 0 O

Fetch 1 0.00 0.00 0 55 3 2

total 3 0.00 0.00 0 55 3 2

Misses in library cache during parse: 1


Optimizer hint: CHOOSE
Parsing user id: 9 (EXAMPLE)
Rows Execution Plan
O SELECT STATEMEIVT OPTIMIZER HINT: CHOOSE
2 NESTED LOOPS
18 TABLE ACCESS (FULL) OF 'REGISTERED-STUDENTS'
18 TABLE ACCESS (BY ROWID) OF 'STUDENTS'
18 INDEX (UNIQUE SCAN) OF 'SYS_COO859' (IJNIQUE)

*+*************************************************************
872 OracleS PUSeL programming

Sort Option Description


PRSCNT Parse count
PRSCPU Amount of CPU time spent parsing
PRSELA Elapsed time during the parse
PRSDSK Number of disk reads during the parse
PRSQRY Number of consistent block reads during the parse
PRSCU Number of current block reads during the parse
PRSMIS Number of library cache misses during the parse
EXECNT Number of executes
EXECPU Amount of CPU time spent during the execute
EXEELA Elapsed time during the execute
EXEDSK Number of physical disk reads during the execute
EXEQRY Number of consistent block reads during the execute
EXECU Number of current block reads during the execute
EXEROW Number of rows processed during the execute
EXEMIS Number of library cache misses during the execute
FCHCNT Number of fetches
FCHCPU Amount of CPU time spent during the fetch
FCHELA Elapsed time during the fetch
FCHDSK Number of physical disk reads during the fetch
FCHQRY Number of consistent block reads during the fetch
FCHCU Number of current block reads during the fetch
FCHROW Number of rows fetched

TABLE 22-2. TKpRoF Sorr options

The statement itself, statistics about the execution of the statement, and
the execution plan are all included in the output. ln this case, no CpU
time
was necessary for the pa.rse, since this statement had been executed already
and thus was already in the shared pool. (The vgsql and vgsqlarea data
Chapter 22: Performance and Tuning 873

dictionary views can be used to see which statements are currently in the
shared pool.) Note, however, that the parse count is still .1, because a parse
call was issued to the database.

NOTE
The trace file vyill also show the recursive
SQL -sfaferrrents generated automatically by
your SQL. For any given statement, Oracle
can is-sue Ltp to six recursive statements,
which do such things as check fhe NIS
-setting;-sand veriiy object privileges. Because
the trace file shows all of the SeL
-srale/lte/l[-s, recursive and otherwise, it can
be a useftrl debugging technique.

SQL-Station Plan Analyzer


The Plan Analvzer component of sel-station provides the functionality
of both the EXPLAIN PLAN statement and TKpRoF. when you first srait
Plan Analyzer and connect to the database,
)iou are presented with two
windows. The upper window allows you to enter a sel statement, and
the lower windorv rvill show the execution pl.rn for the statenrent.
Above the sQL w'indow is a toolbar that allor,vs you to select different
optimizer nrodes. The Analyzer with the sel statement entered is shown
in Figure 22-2.

TIP
When )/ou enter f/re -stafenrent, do not enter
the trailing senticolon. li it is there, it *i!l be
-sent fo t/re d.rt.lb.r.se.?-s part oi the -st.tlente/lt.
Ihe dat.lb.r-se. rr i// then retLtrn "ORA-\)l l:
i nv,t I i d ch.?r.t(-le./. "

ln order to r.'ier..'a pl.rrr, sinrpl,v click on the toolb.rr button ior the
desired platl, tlt.select frotrt the Pl.tn nrenu. Yorr c.lr.r choose lr rrrtr tlrree
defaLrlt optinrizer nrodes or sLrpplr,\,our.o\\,rr with optinrizer hints. The
def.rult pl.rns inclLrde the Rule-based optinrizer.rnd the First Rol,r,and All
R.orvs cost-based optinrizer r.r.rodes. FigLrre )2-3 sholvs the Rule-based
optinrizet' pl.rn for the SQL st.ttentent.
ltr.
#
1F

#+

87 4 Oracle8 PUSQL Programrning F.+

H
:k

e+he SgL Pl{ Pslormhcs Edports gew Wi,Fdow Hdp


ii
ih
{:

Fi:
6.i
ti!::
tu
i:*
I;'

,l.l

j.

:1

FIGURE 22-2. Plan Analyzer with a SeL statement entered

FFOM redislcrad_studa{r rs. dudcnts


\/itlEFf
rs rtudenld-stuchnt$ id

LF .fii{ass ri 1 rt Ll st L:GL{plt_RI ajiSTE; :i aj -1 I I f []t r:T:r


LE ACCESS [AY ROW}DI of E)GMPLE.STUDENTS
I'llOUE INDEX IUNIOUE SCANI of DGfiPLE.SYS_C00IB60DI

FIGURE 22-3. The Rule-based execution plan


Chapter 22: Performance and Tuning 875

ln order to look at the execution statistics for the statement, select the
statistics option from the Performance menu. This allows you to view the
statistics for different optimizer modes and compare the results in either a
graphical or textual format. Figure 22-4 shows the statistics window with
results from the Rule-based optimizer. Results from the other optimizer
modes can be viewed by clicking on the appropriate tabs in the window,
and the compare button will allow you to view side-by-side comparisons
for the different modes.
The Plan Analyzer has many more features than those shown here.
other features include the ability to analyze database objects used in a
given statement and step through each step of a given plan, with an
explanation for each one. For more information about plan Analyzer and its
features, see the online SQl-Station documentatron.

Using the Plan


The execution plan is the same for a given sQL statement and optimizer
mode regardless of how it is determined. A plan is composed of individual

ih Ai l lalue
Iirne 5;c :lepsed Ll Ll5 UOF
otil L.FL I L-ro tl Llo
rlus Fleed LI rl
lrQ hend {4
LrgU e Ll Ll

ll0Dh srreIbyles]
I'les-rrqirg lsltsbBse LBlls -l
,lLr Lrt sttstenrents
0 Ll
rEtlE\;l rLrils L]

lshed HLrNs Ll
lompu,u I
L]

all J tsble S!-ins

Help I chse I

FIGURE 22-4. Execution -stat6ric.s


87 5 Oracle8 PUSQL Programming

operations. Operations (such as a full table scan or an index scan) are


executed individually. Each operation will produce as output a set of rows.
The operation can determine this set by querying a table or index or by
accepting the output of another operation. The results of the final operation
are the result set of the query. Each operation does some of the work for the
entire query. A full discussion of the operations and their efficiency is
beyond the scope of this chapter. We will briefly discuss some of the main
operations, however. For more information , see Oracle Server Tuning.
The execution plan in the prior two sections has three operations:
a NESTED LOOP operation and two different kinds of TABLE
ACCESS operations.

NESTED LOOP
A NESTED LOOP operatiort combines the result sets from two other
operations a and b. Rows in a and b are compared according to a
condition, and those that match are kept. ln this case, the condition is
tlre loin condition "rs.student-id = students.id" given in the statement.
Whenever a join is used, a NESTED LOOP operation is required
for execution.

TABLE ACCESS(FULL)
A fLrll table scan simply retrieves all rows from a table. ln this case, a full
table scan is necessary for registered-students since there are no indexes
created on this table.

TABLE ACCESS (BY ROWID)


This operation is the fastest way to retrieve an individual row. ln this case,
the results from the INDEX SCAN operation are sent to the TABLE ACCESS
BY ROWID operation. The index scan is available sincethere is an index
defined for the lD column of students.

Network lssues
Once the SQL statement has made it to the clatabase, the structure of the
statement determines the execution plan and the resultant performance.
However, the SQL st:ltemcni has to lle sent to the rlatabase first, from the
Llser process to the shadow process. Even if tlre user and shaclow processes
Chapter 22: Performance and Tuning 877

are running on the sarne machine, the SQL staternent or PL/SQL block still
needs to be passed between them. ln most applications, the majority of the
processing time is spent in network transmission. Thus, reducing network
traffic is a prime component for application tuning. There are three
techniques available for reducing network traffic: using client-side PLlSeL,
avoiding unnecessary reparsing, and using the Oracle array interface.
Although the latter two methods are primarily applicable when using OCI
or the Oracle precompilers, they are still relevant to PL/SQL.

Using Client-side PUSQL


lf the application is written using the Developer or Designer 2000 suite of
tools, then there is a PL/SQL engine on the client. This execution
environment is discussed in more detail in Chapter 13. Any work that can
be done on the client reduces the load on the server. ln addition, if many
users are running the application simultaneously, they can process on their
individual client machines in parallel without bogging down the server.
This PL/SQL engine should be used as much as possible. For example,
validation of input data can be done before the data is sent to the server.

Avoiding Unnecessary Reparsing


When a SQL statement or PL/SQL block is sent fronr the client to the server,
the client can keep a reference to the parsed statenrent. This reference is the
cursor data area when using OCI or the cursor cache entry when using the
precompilers. lf your application issues the same statement more than
once, it only needs to parse the statement the first time. For all subsequent
executions, the original parsed statement can be used, possibly with
different values for the bind variables.
This technique is available primarily with OCI or the precompilers,
since they give you more control over cursor processing. ln the OCl,
cursors are controlled directly with a cursor data area. You make explicit
calls to parse (oparse) ancl execute (oexec) the statement. With the
precompilers, the HOLD_CURSOR and RELEASE CURSOR options control
the behavior.
The most obvious place for this technique clirectly in PL/SQL is with the
DBMS_SQL package, where the interface is similar tc OCl. Once a
statement is parsed with DBMS_SQL.PARSE, it can be executed mLrltiple
times. For more information, see Chapter 15.
878 Oracle8 PUSQL Programming

Array Processing
The precompilers and oCl can send and retrieve data using host arrays.
This technique is known as the oracle array interface. Thiiis a very useful
method, since it allows large amounts of data to be sent over the network
as one unit, rather than in several trips. For example, if you are retrieving
.100
rows, you can do it in one fetch, which brings back all 100 rows,
ratherthan in lo0fetches, each of which returns one row. This approach is
used in SQL*PIus.
PL/SQL does not directly use the oracle array interface, since it does
not store arrays the same way as a host array. pL/SeL tables are
implemented differently, as discussed in Chapter 3. However, the array
interface should be used whenever possible. lf you are issuing pl/sel
commands from oCl or the precompilers, use the array interfice for the
other sQL statements in your application. For more information on how to
use host arrays, consult the precompiler and/or OCI documentation.

Summarrf

or the TKPROF utility. We also discussed how to minimize network traffic


by using client-side PL/SQL, avoiding unnecessary reparsing, and using the
Oracle array interface.

You might also like