The UTL_REF package provides a PL/SQL interface that allows you to select and modify objects (instances of an object type) in an object table without having to specify or know about the underlying database table. With UTL_REF, you only need a reference to the object in order to identify it in the database and perform the desired operations. With UTL_REF, you can do any of the following:
Select or retrieve an object from the database
Lock an object so that no other session can make changes to the object
Select and lock an object in a single operation (similar to SELECT FOR UPDATE)
Update the contents of an object
Delete an object
You will typically use UTL_REF programs when you have references to an object and one of the following is true:
You do not want to have to resort to an SQL statement to perform the needed action.
You do not even know the name of the table that contains the object, and therefore cannot rely on SQL to get your job done.
Before getting into the details, let’s start with an initial example of how you might use the UTL_REF packages.
You will be able to use UTL_REF programs only to select or modify objects in an object table. An object table is a table in which each row of the table is an object. Here are the steps one might take to create an object table.
First, create an object type:
CREATE TYPE hazardous_site_t IS OBJECT ( name VARCHAR2(100), location VARCHAR2(100), dixoin_level NUMBER, pcb_level NUMBER, METHOD FUNCTION cleanup_time RETURN NUMBER);
Now you can create a table of these objects:
CREATE TABLE hazardous_sites OF hazardous_site_t;
As you will see in the headers for the UTL_REF programs, Oracle has provided a special parameter-passing syntax called ANY. This syntax allows us to pass references and objects of any object type in and out of the programs. This behavior is not otherwise available in Oracle8 built-in packages or the code that you yourself can write using object types.
The UTL_REF package is created when the Oracle8.0.4 (or later) database is installed. The utlref.sql script (found in the built-in packages source code directory, as described in Chapter 1) contains the source code for this package’s specification. The script is called by catproc.sql, which is normally run immediately after the database is created. The script creates the public synonym UTL_REF for the package and grants EXECUTE privilege on the package to public. All Oracle users can reference and make use of the package.
Every program in this package runs as “owner.” This means that programs in the UTL_REF package operate within the privileges of the session running those programs. You will be able to select and modify only objects to which your session has been granted the necessary privileges.
Table 9.5 lists the programs defined for the UTL_REF packages.
Table 9-5. UTL_REF Programs
Name |
Description |
Use in SQL |
---|---|---|
DELETE_OBJECT |
Deletes an object from the underlying object table |
No |
LOCK_OBJECT |
Locks an object so that another session cannot change the object |
No |
SELECT_OBJECT |
Selects an object based on its reference, returning that object as an OUT argument |
No |
UPDATE_OBJECT |
Updates the object specified by the reference by replacing it with the object you pass to the program |
No |
UTL_REF does not declare any nonprogram elements.
UTL_REF does not declare any exceptions. However, you may encounter any of the following Oracle exceptions when running the UTL_REF programs:
Insufficient privileges. You must have the appropriate privileges on the underlying database table.
Insufficient privileges. You attempted to update an object table on which you have only SELECT privileges. You must have the appropriate privileges on the underlying database table.
Cannot serialize access for this transaction. You have tried to change data after the start of a serialized transaction.
Deadlock detected while waiting for resource. Your session and another session are waiting for a resource locked by the other. You will need to wait or ROLLBACK.
No data found. The REF is NULL or otherwise not associated with an object in the database.
This section describes the programs available through the UTL_REF package. A single, extended example at the end of the chapter shows how you might be able to take advantage of the UTL_REF programs in your own applications.
Use the DELETE_OBJECT procedure to delete an object (actually, the row containing that object) specified by the given reference. The header is,
PROCEDURE UTL_REF.DELETE_(reference IN REF ANY);
where reference identifies the object.
This program effectively substitutes for the following kind of SQL statement:
DELETE FROM the_underlying_object_table t WHERE REF (t) = reference;
In contrast to this SQL statement, with DELETE_OBJECT you will not need to specify the name of the underlying database object table to retrieve the object.
Use the LOCK_OBJECT procedure to lock or lock and retrieve an object for a given reference. The header is overloaded as follows:
PROCEDURE UTL_REF.LOCK_OBJECT (reference IN REF ANY); PROCEDURE UTL_REF.LOCK_OBJECT (reference IN REF ANY ,object IN OUT ANY);
Parameters are summarized in the following table.
Parameter |
Description |
---|---|
reference |
The reference to the object |
object |
The value of the object selected from the database (if supplied) |
If you call LOCK_OBJECT and do not provide a second argument, then the object will be locked, but that object will not be returned to the calling program.
This program effectively substitutes for the following type of SQL statement:
SELECT VALUE (t) INTO object FROM the_underlying_object_table t WHERE REF (t) = reference FOR UPDATE;
In contrast to this SQL statement, with LOCK_OBJECT you will not need to specify the name of the underlying database object table to retrieve the object.
It is not necessary to lock an object before you update or delete it. By requesting a lock, however, you ensure that another session cannot even attempt to make changes to that same object until you commit or roll back.
Use the SELECT_OBJECT procedure to retrieve an object for a given reference. The header follows:
PROCEDURE UTL_REF.SELECT_OBJECT (reference IN REF ANY ,object IN OUT ANY);
Parameters are summarized in the following table.
Parameter |
Description |
---|---|
reference |
The reference to the object |
object |
The value of the object selected from the database |
This program effectively substitutes for the following type of SQL statement:
SELECT VALUE (t) INTO object FROM the_underlying_object_table t WHERE REF (t) = reference;
In contrast to this SQL statement, with SELECT_OBJECT you will not need to specify the name of the underlying database object table to retrieve the object.
Note the following restrictions on calling SELECT_OBJECT:
The program does not assert a purity level with the RESTRICT_REFERENCES pragma.
You cannot call this program from within an SQL statement, either directly or indirectly.
Use the UPDATE_OBJECT procedure to replace an object in the database specified by a given reference with your “replacement” object. Here’s the header:
PROCEDURE UTL_REF.UPDATE_OBJECT (reference IN REF ANY ,object IN ANY);
Parameters are summarized in the following table.
Parameter |
Description |
---|---|
reference |
The reference to the object |
object |
The object that is to be placed in the row of the object table specified by the reference |
This program effectively substitutes for the following type of SQL statement:
UPDATE the_underlying_object_table t SET VALUE (t) = object WHERE REF (t) = reference;
In contrast to this SQL statement, with UPDATE_OBJECT you will not need to specify the name of the underlying database object table to retrieve the object.
Let’s start with an object type that can hold various types of documents
CREATE OR REPLACE TYPE Document_t AS OBJECT ( doc_id NUMBER, author VARCHAR2(65), created DATE, revised DATE, body BLOB, MEMBER PROCEDURE update_revised ); /
To keep this example simple, we’ll implement only a single object method:
CREATE OR REPLACE TYPE BODY Document_t AS MEMBER PROCEDURE update_revised IS BEGIN revised := SYSDATE; END; END; /
Here’s a table that will hold any kind of document:
CREATE TABLE documents OF Document_t;
We might have a requisition type that has a special type of document. Each requisition contains a REF to a particular document.
CREATE OR REPLACE TYPE Requisition_t AS OBJECT ( doc_ref REF Document_t, needed DATE, approved DATE, MEMBER PROCEDURE update_revision_date, MEMBER FUNCTION has_valid_need_date RETURN BOOLEAN ); /
In a moment, we’re going to look at an example of UTL_REF that implements the type body of Requisition_t. But let’s first look at life without UTL_REF. Not only do we have to write SQL, we also have to know the table name in each statement where we need access to a persistent object. In fact, the following methods are hard-coded to work with only one particular table implementation (not good):
CREATE OR REPLACE TYPE BODY Requisition_t AS MEMBER FUNCTION has_valid_need_date RETURN BOOLEAN IS document Document_t; CURSOR doc_cur IS /* Ugly! */ SELECT VALUE(d) FROM documents d WHERE REF(d) = SELF.doc_ref; BEGIN OPEN doc_cur; FETCH doc_cur INTO document; /* Ditto */ CLOSE doc_cur; IF document.created > SELF.approved THEN RETURN FALSE; ELSE RETURN TRUE; END IF; END; MEMBER PROCEDURE update_revision_date IS BEGIN UPDATE documents d /* Even uglier */ SET revised = SYSDATE WHERE REF(d) = SELF.doc_ref; END; END; /
Let’s turn now to see what UTL_REF can do for us:
CREATE OR REPLACE TYPE BODY Requisition_t AS MEMBER FUNCTION has_valid_need_date RETURN BOOLEAN IS document Document_t; BEGIN /* UTL_REF.SELECT_OBJECT allows us to retrieve the document object || from persistent database storage into a local variable. No muss, || no fetch, no bother! SELECT_OBJECT finds the table and object || for us. */ UTL_REF.SELECT_OBJECT (SELF.doc_ref, document); /* Now that we have retrieved the document object, we can || easily gain access to its attributes: */ IF document.created > SELF.approved THEN RETURN FALSE; ELSE RETURN TRUE; END IF; END; MEMBER PROCEDURE update_revision_date IS document Document_t; BEGIN /* To update the revision date of the requisition object, || we'll simply "delegate" to the referenced document. || First we retrieve it... */ UTL_REF.SELECT_OBJECT (SELF.doc_ref, document); /* ...then we can invoke a method on the newly retrieved || (but transient) object. Notice that we do NOT update || the attribute directly, but rely instead on the public || method supplied for this purpose. */ document.update_revised; /* ...and now we easily update the data in the underlying table || (whatever table it is...we don't know or care!) */ UTL_REF.UPDATE_OBJECT(SELF.doc_ref, document); END; END; /
Since UTL_REF frees us from dependence on the specific underlying table, it allows us to achieve greater reuse, portability, modularity, and resilience to change.