Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

Oracle

Solutions for High-End


Oracle® DBAs and Developers Professional

Welcome to the
World of Oracle10g
PL/SQL!
Steven Feuerstein and Chip Dawes
In this article, Steven Feuerstein and Chip Dawes offer an overview of the new
Oracle10g PL/SQL features. In future issues of Oracle Professional, they’ll explore
the most interesting or non-trivial of these in more detail.

N
OW is the time for all PL/SQL developers to take a deep breath, let it
out slowly, and thank their lucky stars they’re not DBAs. Oracle has
announced some sweeping changes in database administration with
Oracle10g—namely, that it’s on its way to being a self-managing database! May 2004
While it’s doubtful that many experienced DBAs are worried about Oracle10g Volume 11, Number 5
making them redundant any time soon, it’s not a particularly good sign when
Oracle more or less declares “feature war” on your area of expertise. 1 Welcome to the World of
And even if the self-managing part is a ways off, Oracle10g still (as is the Oracle10g PL/SQL!
Steven Feuerstein and Chip Dawes
case with every new version of the database) requires significant re-tooling
of a DBA’s brains and scripts. Not so for PL/SQL developers. Oracle10g 7 Object Types and Inheritance,
certainly offers us some interesting new features and major new architectural Part 2: Input Channels and
improvements, but almost everything we’ve learned over the years remains Data Transformation
Gary Menchen
relevant—and there isn’t too much new content to absorb.
Oracle10g has several new features that PL/SQL programmers can use 12 Using the Oracle Trace Facility
to improve their programs. The new quoting mechanism will enable you to Dan Hotka
write string literals having embedded quotation marks in a more easily
16 May 2004 Downloads
readable structure. There are new floating point datatypes that offer improved
performance for non-financial calculations. Integer datatypes have a more
efficient internal representation that should improve performance of programs
using them. Compilation has been improved to provide compile-time
warnings, improved optimization, and native compilation. Indicates accompanying files are available online at
www.pinnaclepublishing.com.
There’s a series of new nested table functions that give you, the
programmer, much greater capabilities to use nested that are concerned with rounding errors or require
tables in relational ways. Bulk binds have been improved decimal precision should probably not use these floating
so that you can use sparse collections with them. Regular point datatypes.
expressions have been introduced in Oracle10g and are a
powerful addition to the PL/SQL programmer’s toolkit. You gotta love it: Compile-time warnings!
Oracle10g lets you enable additional compile-time
Programmer-defined quoting mechanism warnings to help make your programs more robust. These
Oracle10g allows you to define your own quoting warnings highlight potential problems that aren’t severe
mechanism for string literals in both SQL and PL/SQL. enough to raise an exception, but may result in runtime
Use the characters q' (q followed by a single quote) to errors or poor performance.
note the programmer-defined delimiter for your string To enable these warnings, you need to set the
literal. Oracle will interpret the character following database initialization parameter plsql_warnings. This
the q' as the delimiter for your string. NCHAR and parameter can be set globally in the spfile, in your session
NVARCHAR delimiters are preceded with the letters with the ALTER SESSION command, or with the built-in
nq, as in nq'^nchar string^'. procedure dbms_warning. The plsql_warnings parameter
This technique can simplify your code when single is a comma-delimited list of values, each of which has
quotes appear within a string, such as the literals in a SQL the syntax:
statement. If you define your delimiter with one of the
four bracketing characters—( [ { <—then you’ll need to [ENABLE | DISABLE | ERROR]:
[ALL|SEVERE|INFORMATIONAL|PERFORMANCE|warning_number]
use the right-hand version of that bracketing character as
the closing delimiter. For example, q' [ will need to be For example, to enable all warnings in your
closed with ]'. Table 1 displays some examples of using session, execute:
this new quoting mechanism.
ALTER SESSION SET plsql_warnings = 'enable:all'
Integer and floating point datatype improvements
Oracle provides a variety of datatypes to store 32-bit If you want to enable warning message number
whole numbers: BINARY_INTEGER, INTEGER, INT, 06002 and all warnings in the performance category
SMALLINT, NATURAL, NATURALN, POSITIVE, except warning number 07202, execute:
POSITIVEN, SIGNTYPE, and PLS_INTEGER. Prior to
ALTER SESSION SET plsql_warnings = 'enable:06002'
Oracle10g, all of these, except PLS_INTEGER, were ,'enable:performance'
manipulated using the same C-language arithmetic ,'disable:07202';

library as the NUMBER datatype. PLS_INTEGER, though,


uses machine arithmetic, which is up to three times faster To see what your current setting is, you can execute:
than library arithmetic. In Oracle10g, the distinction
DBMS_OUTPUT.PUT_LINE(
among these datatypes has been eliminated, and all of DBMS_WARNING.GET_WARNING_SETTING_STRING());
these whole number datatypes now use the speedier
machine arithmetic. Binary integer datatypes store signed Some examples of warnings follow:
31 31
integers in the range of -2 + 1 to 2 - 1. The subtypes
SQL> ALTER SESSION SET plsql_warnings = 'enable:all';
include NATURAL (0 through 231) and POSITIVE
31
(1 through 2 ) together with the NOT NULL variations Session altered.

NATURALN and POSITIVEN. SIGNTYPE is restricted to SQL> CREATE OR REPLACE PACKAGE create_policy IS
2 PROCEDURE process_dec_page ( dec_page IN OUT CLOB );
three values (-1, 0, 1). PLS_INTEGER is an unconstrained 3 END create_policy;
subtype (alias) of BINARY_INTEGER. 4 /

Oracle10g introduced IEEE 754 compliant floating SP2-0808: Package created with compilation warnings
point numbers to both SQL and PL/SQL. These subtypes SQL> show err
are the single precision BINARY_FLOAT and the double Errors for PACKAGE CREATE_POLICY:
precision BINARY_DOUBLE. These datatypes require less LINE/COL ERROR
memory and use native machine arithmetic, thus -------- --------------------------------------------
2/32 PLW-07203: parameter 'DEC_PAGE' may benefit from
performing better for scientific or engineering use of the NOCOPY compiler hint
applications that are computation-
intensive or require comparison to Table 1. Examples of using the new quoting mechanism.
infinity or NaN (Not a Number).
These two datatypes have binary Literal using old quoting mechanism Literal using new quoting mechanism Actual value
'TZ=''CDT6CST''' q'(TZ='CDT6CST')' TZ='CDT6CST'
precision instead of the decimal
'''' q'-'-' '
precision used in the NUMBER 'It''s here' q'[It's here]' It's here
family. So, financial applications '''''' nq'^''^' ''

2 Oracle Professional May 2004 www.pinnaclepublishing.com


SQL> CREATE OR REPLACE PACKAGE BODY create_policy IS • Aggressive optimization beyond level 1, including
2 PROCEDURE process_dec_page (
3 dec_page IN OUT NOCOPY CLOB ) IS rearranging source code (this is the default setting)
4 BEGIN
5 default_dec(dec_page);
6 END process_dec_page; These settings are also modifiable for the current
7 END create_policy;
8 / session; for example:
SP2-0810:Package Body created with compilation warnings
ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 0;
SQL> show err
Errors for PACKAGE BODY CREATE_POLICY:
Oracle retains optimizer settings on a module-by-
LINE/COL ERROR
-------- -------------------------------------------- module basis. When you recompile a particular module
3/6 PLW-05000: mismatch in NOCOPY qualification
between specification and body
with non-default settings, the settings will “stick,”
allowing you to recompile later using REUSE SETTINGS.
SQL> CREATE OR REPLACE PROCEDURE dead_code IS
2 x NUMBER := 10; For example:
3 BEGIN
4 IF x = 10 THEN
5 x := 20; ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL=0;
6 ELSE
7 x := 100; -- dead code
8 END IF; and then:
9 END dead_code;
10 /
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;
SP2-0804:Procedure created with compilation warnings

SQL> show err To view all of the compiler settings for your modules,
Errors for PROCEDURE DEAD_CODE:
including optimizer level, interpreted vs. native, and
LINE/COL ERROR compiler warning levels, query the USER_PLSQL_
-------- --------------------------------------------
7/7 PLW-06002: Unreachable code OBJECT_SETTINGS view.

The optimizing compiler Native compilation of PL/SQL


PL/SQL’s optimizing compiler, also introduced in In Oracle10g, native compilation is much simpler to
Oracle10g, can improve runtime performance configure, in part because Oracle now uses database
dramatically, imposing a relatively slight overhead at tables rather than files for permanent storage of the
compile time. Fortunately, the benefits of optimization generated shared libraries. Oracle also limits support to
apply both to interpreted and natively compiled PL/SQL, the C compiler supplied by the platform vendor. To use
because optimizations are applied by analyzing patterns native compilation in Oracle10g:
in source code. 1. Inspect the file $ORACLE_HOME/plsql/spnc_
The optimizing compiler is enabled by default. commands and, if necessary, revise the path to the
However, you may wish to alter its behavior, either by supported C compiler.
lowering its aggressiveness or by disabling it entirely. 2. Set the parameter PLSQL_NATIVE_LIBRARY_DIR as
For example, if, in the course of normal operations, your above and create this directory if it doesn’t exist. This
system must perform recompilation of many lines of code, is the location in the file system that Oracle will use
or if an application generates many lines of dynamically for an on-demand cache of the shared library files.
executed PL/SQL, the overhead of optimization may be 3. If your system has more than 15,000 PL/SQL objects,
unacceptable. Keep in mind, though, Oracle’s tests show set PLSQL_NATIVE_DIR_SUBDIR_COUNT as above.
that the optimizer doubles the runtime performance of 4. Either make a global change so that the database
computationally intensive PL/SQL. parameter PLSQL_CODE_TYPE is set to NATIVE, or
In some cases, the optimizer may even alter program issue session-level statements of the form 'ALTER
behavior. One such case might occur in code written SESSION SET PLSQL_CODE_TYPE = 'NATIVE';.
for Oracle9i that depends on the relative timing of 5. Create or replace your stored programs.
initialization sections in multiple packages. If your testing
demonstrates such a problem, yet you wish to enjoy the Nested table functions
performance benefits of the optimizer, you may want to There are several functions that can be used to manipulate
rewrite the offending code or introduce an initialization collections. Most of these are new to Oracle10g; only
routine that ensures the desired order of execution. CAST and MULTISET are available in earlier releases. The
To change the optimizer settings for the entire COLLECT, POWERMULTISET, and POWERMULTISET_
database, set the database parameter PLSQL_OPTIMIZE_ BY_CARDINALITY are only valid in a SQL statement;
LEVEL. Valid settings are: they cannot be used, for example, in a PLSQL assignment.
• No optimization Table 2 describes these collection (multiset) functions.
• Moderate optimization, such as eliminating The CAST function works together with the
superfluous code or exceptions COLLECT and MULTISET functions. MULTISET was

www.pinnaclepublishing.com Oracle Professional May 2004 3


available prior to Oracle10g and operates on a subquery. answer := nt2 MULTISET INTERSECT nt3; -- (3,2,1)
answer := nt2 MULTISET INTERSECT DISTINCT nt3;
COLLECT is new to Oracle10g and operates on a column -- (3,2,1)
in a SQL statement: answer := nt3 MULTISET EXCEPT nt2; -- (3)
answer := nt3 MULTISET EXCEPT DISTINCT nt2; -- ()
CREATE TYPE email_list_t AS TABLE OF VARCHAR2(64); answer := SET(nt3); -- (2,3,1)
-- COLLECT operates on a column IF (nt1 IS A SET) AND (nt3 IS NOT A SET) THEN
SELECT CAST(COLLECT(cust_email)AS email_list_t) dbms_output.put_line('nt1 has unique elements');
FROM oe.customers; dbms_output.put_line('but nt3 does not');
END IF;
-- which is equivalent to
SELECT CAST(MULTISET(SELECT cust_email IF (nt3 MULTISET EXCEPT DISTINCT nt2) IS EMPTY THEN
FROM oe.customers) dbms_output.put_line('empty set');
AS email_list_t) END IF;
FROM dual;
IF 3 MEMBER OF (nt3 MULTISET EXCEPT nt2) THEN
dbms_output.put_line('3 is in the answer set');
Examples of the other nested table functions, END IF;

operators, and expressions are demonstrated as follows: IF nt1 SUBMULTISET nt3 THEN
dbms_output.put_line('nt1 is a subset of nt3');
END IF;
DECLARE
TYPE nested_type IS TABLE OF NUMBER; IF SET(nt3) IN (nt1,nt2,nt3) THEN
nt1 nested_type := nested_type(1,2,3); dbms_output.put_line(
nt2 nested_type := nested_type(3,2,1); 'expression is IN the list of nested tables');
nt3 nested_type := nested_type(2,3,1,3); END IF;
nt4 nested_type := nested_type(1,2,4); END;
answer nested_type;
BEGIN
answer := nt1 MULTISET UNION nt4; -- (1,2,3,1,2,4) Improvements to bulk binds
answer := nt1 MULTISET UNION nt3; -- (1,2,3,2,3,1,3) You can use collections to improve the performance of
answer := nt1 MULTISET UNION DISTINCT nt3; -- (1,2,3) SQL operations executed iteratively by using bulk binds.

Table 2. New collection functions.

Function Return value Description


= BOOLEAN Compares two nested tables and return TRUE if they have the same named type,
cardinality, and the elements are equal.
<> BOOLEAN Compares two nested tables and return FALSE if they differ in named type, cardinality, or
equality of elements.
[NOT] IN ( ) BOOLEAN Returns TRUE [FALSE] if the nested table to the left of IN exists in the list of nested tables
in the parentheses.

CARDINALITY(x) NUMBER Returns the number of elements in varray or nested table x. Returns NULL if the collection
is atomically NULL (not initialized).
CAST(k AS t) TYPE t Changes the data type of k to type t—used in conjunction with collect or multiset.

COLLECT (Oracle10g) NESTED TABLE Used in conjunction with CAST to map a column to a collection.
MULTISET NESTED TABLE Used in conjunction with CAST to map a subquery to a collection.
x MULTISET EXCEPT [DISTINCT] y NESTED TABLE Performs a MINUS set operation on nested tables x and y, returning a nested table whose
elements are in x, but not in y. x, y, and the returned nested table must all be of the
same type. The DISTINCT keyword forces the elimination of duplicates from the
returned nested table.

x MULTISET INTERSECT [DISTINCT] y NESTED TABLE Performs an INTERSECT set operation on nested tables x and y, returning a nested table
whose elements are in both x and y. x, y, and the returned nested table must all be of the
same type. The DISTINCT keyword forces the elimination of duplicates from the returned
nested table.
x MULTISET UNION [DISTINCT] y NESTED TABLE Performs a UNION set operation on nested tables x and y, returning a nested table whose
elements include all those in x as well as those in y. x, y, and the returned nested table
must all be of the same type. The DISTINCT keyword forces the elimination of duplicates
from the returned nested table.
SET(x) NESTED TABLE Returns nested table x without duplicate elements.

x IS [NOT]A SET BOOLEAN Returns TRUE [FALSE] if the nested table x is composed of unique elements.
x IS [NOT] EMPTY BOOLEAN Returns TRUE [FALSE] if the nested table x is empty.
e [NOT] MEMBER [OF] x BOOLEAN Returns TRUE[FALSE] if an expression e is a member of the nested table x.

y [NOT] SUBMULTISET [OF] x BOOLEAN Returns TRUE [FALSE] if nested table y contains only elements that are also in nested
table x.
4 Oracle Professional May 2004 www.pinnaclepublishing.com
Bulk binds reduce the number of context switches allow you to specify a subset of rows in a driving
between the PL/SQL engine and the SQL engine. Two collection that will be used in the FORALL statement. To
PL/SQL language constructs implement bulk binds: match the row numbers in the data collection with the
FORALL and BULK COLLECT INTO. row numbers in the driving collection, use the INDICES
The syntax for the FORALL statement is: OF clause. To match the row numbers in the data
collection with the values found in the defined rows of
FORALL bulk_index IN [lower_bound..upper_bound
| INDICES OF collection_variable
the driving collection, use the VALUES OF clause.
[BETWEEN lower_bound AND upper_bound]
| VALUES OF collection_variable ]
[SAVE EXCEPTIONS] Regular expressions
sql_statement; As we mentioned earlier in this article, Oracle10g
supports the use of regular expressions via four new
Bulk_index can be used only in the sql_statement and
built-in functions: REGEXP_LIKE, REGEXP_INSTR,
only as a collection index (subscript). When PL/SQL
REGEXP_SUBSTR, and REGEXP_REPLACE. This
processes this statement, the whole collection, instead of
section provides only a brief description of how regular
each individual collection element, is sent to the database
expressions work; for a tutorial and quick reference, see
server for processing. To delete all of the accounts in the
the Oracle Regular Expressions Pocket Reference, by Jonathan
collection inactives from the table ledger, do this:
Gennick and Peter Linsley (O’Reilly).
FORALL i IN inactives.FIRST..inactives.LAST
DELETE FROM ledger WHERE acct_no = inactives(i); Metacharacters
Regular expressions are found in Unix utilities such as
With Oracle10g, if there are non-consecutive index grep, sed, and the ex editor, in the Perl scripting language,
values due to deletions, you’ll need to use the INDICES and in many other tools. Regular expressions are a
OF syntax to skip over the deleted elements, like this: powerful and popular means of processing text, mainly
because they use metacharacters to facilitate searching for
FORALL i IN INDICES OF inactives
DELETE FROM ledger WHERE acct_no = inactives(i); strings. The metacharacters supported by Oracle are
shown in the Table 3.
With Oracle10g, if you’re interested in the values of a
sparse collection of integers instead of the indices, you’ll REGEXP_LIKE
need to use the VALUES OF syntax, such as: The REGEXP_LIKE function determines whether a
specific column, variable, or text literal contains text
FORALL i IN VALUES OF inactives_list
-- inactives_list is a collection of index values from matching a regular expression. It returns Boolean TRUE if
-- the inactives table which are earmarked for the regular expression is found in the source_string and
deletion
DELETE FROM ledger WHERE acct_no = inactives(i); FALSE if the regular expression isn’t found. The syntax is:

These new INDICES OF and VALUES OF keywords REGEXP_LIKE (source_string, pattern [,match_modifier])

Table 3. Regular expression metacharacters. where:


• source_string is the character string
Pattern to be searched.
metacharacter Description
• pattern is the regular expression
* Asterisk. Matches zero or more occurrences.
+ Plus sign. Matches one or more occurrences. pattern that Oracle tries to find in
? Question mark. Matches zero or one occurrence. the source_string.
^ Caret. Matches beginning of line. • match_modifier is one or more
$ Dollar sign. Matches end of line.
modifiers that apply to the search;
. Period. Matches any single character.
\ Backslash. Treats the following metacharacter as a nonspecial character. see the “Match modifiers” section
{m} Curly braces. Matches exactly m times. later in the article for information.
{m,} Curly braces. Matches at least m times.
{m,n} Curly braces. Matches at least m times, but no more than n times.
For example:
[] Square brackets. Matches any of the characters in the square brackets.
| Vertical bar. Alternation operator for specifying alternative matches.
() Parentheses. Grouping expression. IF REGEXP_LIKE(phone_number,'^\(?212\)?'
THEN
\n Backslash. Backreference expression (\1 through \9). Used in conjunction -- phone number begins with 212
with ( ) to identify the nth occurrence of the backreferenced expression. -- optionally enclosed by parentheses
[::] Character class. Examples are [:digit:] for numeric digits and [:alnum:] for APPLY_NYC_SURCHARGE;
END IF;
alphanumeric characters.
[..] Collation element. Encloses multiple characters treated as one character
(for example, ’ch’ in Spanish). REGEXP_INSTR
[==] Equivalence class. Matches accented and unaccented versions of a letter. The REGEXP_INSTR function locates, by

www.pinnaclepublishing.com Oracle Professional May 2004 5


character position, an occurrence of text matching a REGEXP_REPLACE
regular expression. It returns the beginning or ending The REGEXP_REPLACE function replaces a regular
position of the regular expression within a string. The expression with new text that you specify. Your
syntax is: replacement text may include back references to values
in the regular expression. The syntax is:
REGEXP_INSTR (source_string
,pattern
[,beginning_position REGEXP_REPLACE (source_string
[,occurrence ,pattern
[,return_option [,replacement_string
[,match_modifier]]]]) [,position
[,occurrence
[,match_modifier]]])
where:
• source_string is the character string to be searched. where:
• pattern is the regular expression pattern that Oracle • source_string is the character string to be searched.
tries to find in the source_string. • pattern is the regular expression pattern that Oracle
• beginning_position is the character position at which tries to find in the source_string.
to begin the search. • replacement_string is the replacement text for pattern.
• occurrence is the ordinal occurrence desired (1 = first, • position is the character position at which to begin
2 = second, and so on). the search.
• return_option is either 0 for the beginning position or • occurrence is the ordinal occurrence desired (0 = all
1 for the ending position. occurrences, 1 = first, 2 = second, and so on).
• match_modifier is one or more modifiers that apply to • match_modifier is one or more modifiers that apply to
the search; see the “Match modifiers” section later in the search; see the “Match modifiers” section of the
the article for information. article for information.

For example: For example:


witty_saying := 'Man fears time, but time fears the Pyramids';
-- change the domain part of the email addresses
-- display the witty_saying
-- replace everything between the @ and the '.com' with
-- starting with the second occurrence of the word 'time'
-- the new domain name
dbms_output.put_line(SUBSTR(witty_saying
dbms_output.put_line(REGEXP_REPLACE(email_address
,REGEXP_INSTR(witty_saying,'time',1,2)));
,'@.*\.com','@new_domain.com'));
time fears the Pyramids
Match modifiers
REGEXP_SUBSTR The match_modifiers available to the regular expression
The REGEXP_SUBSTR function extracts text matching a condition and functions are shown in Table 4.
regular expression from a character column, variable, or
text literal. It returns as many matching substrings as it
finds (which might be zero). The syntax is: Table 4. Match modifiers.

REGEXP_SUBSTR (source_string Match_modifier Description


,pattern i Uses a case-insensitive search. The default behavior
[,position is based on NLS_SORT.
[,occurrence
[,match_modifier]]]) c Uses a case-sensitive search. The default behavior is
based on NLS_SORT.
where: n Enables the dot metacharacter to match newlines.
m Treats the source_string as multiple lines for
• source_string is the character string to be searched. purposes of the beginning and end-of-line
• pattern is the regular expression pattern that Oracle metacharacters ^ and $.
tries to find in the source_string.
• position is the character position at which to begin Smooth sailing
the search. PL/SQL has been around for a while now, and that means
• occurrence is the ordinal occurrence desired (1 = first, three things:
2 = second, and so on). • Most of the basic programming functionality is
• match_modifier is one or more modifiers that apply to well-established. A new version of Oracle is unlikely
the search; see the “Match modifiers” section later in to turn your PL/SQL world upside down.
the article for information. • Many new features round out capabilities in
ways that aren’t often of use to the majority of
For example: programmers. MULTISET functionality may not
-- get the leading number part of the address
mean a whole lot to you—right now. But who
-- (up to a whitespace character) knows what may come in handy next year?
street_number := REGEXP_SUBSTR(address_line1,
'[[:digit:]]+[:space:]'); Continues on page 16

6 Oracle Professional May 2004 www.pinnaclepublishing.com


Oracle
Professional

Object Types and Inheritance,


Part 2: Input Channels and Data
Transformation
Gary Menchen
InputChannels are the reverse of OutputChannels, which Gary member procedure open,
-- set characters that delimit lines, for the
Menchen discussed in the April issue of Oracle Professional. -- purposes of readln. When newLineChars is null
-- then the three different possibilities are used.
With object types for both input and output, we can develop -- 1st chr(13) ||chr(10) 2nd Chr(13) 3rd chr(10)
methods for I/O that perform complex transformations on the -- If setNewLineChar is invoked then only the
-- characters passed via setNewLineChar are used.
streams of data being processed in our programs. -- This procedure is additive: once invoked other
-- characters may be added by repeated invocations.
member procedure setNewLineChar( pNewLine in

I
N the April 2004 issue of Oracle Professional, I varchar2),
member function getNewLineTokens return stringList,
developed a hierarchy of object types that performed final member procedure initInputChannel
streaming output to various kinds of destinations— ) NOT INSTANTIABLE NOT FINAL
CLOB, BLOB, DBMS_OUTPUT, and so on—and ended /

with a note that there were other types of OutputChannel


descendents that could be created, but which were There are two main issues to be faced when dealing
pointless without a corresponding InputChannel. Pipes with reading unstructured data from a source. First, you
would be one example, but more to the point would be must decide what to do when there’s nothing available to
channels that transform the data, such as encryption/ be read. This might signal that all contents of the data
decryption or compression/expansion. In this article I’ll source have been “consumed” (all of a LOB has been
describe the parent InputChannel type, write descendents read), or it might be a temporary condition (a pipe is
for reading from BLOB and CLOB, and then create input empty, but perhaps more content will be sent down the
and output types that perform DES encryption and pipe later). I briefly considered having a function that
decryption utilizing the DBMS_OBFUSCATION_ would indicate an “out of data” condition, but discarded
TOOLKIT Oracle supplied package. that idea in favor of expecting a NO_DATA_FOUND
exception when a read function finds no data at all—
Creating the parent type: InputChannel this is both simple and standard practice in Oracle
The InputChannel object type is the parent type of the programming. Since reading is done in the descendents of
object types that will be created to implement reading InputChannel, it’s not possible to enforce any particular
from various sources of stream-like data (see Listing 1). behavior at the level of the parent type.
The second issue has to do with reading lines of text.
LOBs very often contain text that needs to be processed
Listing 1. Declaring the InputChannel object type. line by line. Lines of text are usually separated by line
termination characters that may be either a CHR(13)
CREATE OR REPLACE
TYPE INPUTCHANNEL carriage return (the Mac standard prior to OS/X), a
AS OBJECT
-- InputChannel is a parent class for various kinds of CHR(10) linefeed (the Unix standard), or both (Windows’
-- input that perform reads gift to the world). Someone should attempt a study of
-- open and close are instantiated here, but do
-- nothing for those descendents that do not require what this variance has cost the industry over the past 20
-- any specific activity to open or close
(
years, but meanwhile we have to deal with it. My
newLineChars StringList, preference is for maximum flexibility, so by default we
member procedure close,
not instantiable not final member function should be prepared to recognize any of the standard line
read( self in out nocopy InputChannel,
pCount in number ) return varchar2,
termination characters.
not instantiable not final member function I’ve implemented this by the following:
readLn( self in out nocopy InputChannel)
return varchar2, 1. The available new-line characters are stored in a
not instantiable not final member function collection type StringList, a VARRAY of VARCHAR2
readRaw(self in out nocopy InputChannel,
pCount in number) return raw, (see the accompanying Download for its declaration).

www.pinnaclepublishing.com Oracle Professional May 2004 7


This is initialized (in initInputChannel) to an The constructor function takes a LOB locator as its
empty array. parameter. It stores it in the the_clob attribute, and sets
2. The member procedure setNewLineChar is used to ptr to 1, the beginning position within the CLOB. It then
specify a character or combination of characters that calls the initClobInputChannel procedure. That procedure
are to be recognized as line delimiters. This procedure has nothing to do inside ClobInputChannel, but it does
may be invoked repeatedly, each time adding another call its parent’s initialization procedure, InitInputChannel.
line terminator. The ptr attribute is used to track the current position
3. The member function getNewLineTokens returns the in the CLOB. There’s no procedure to change its value
VARRAY of line termination characters. If it’s null, since I’m taking a rather abstract view of these object
meaning that no specific line termination characters types, and treating both input and output as one-
have been passed, then all three are returned, as in: directional. There is, however, given Oracle’s design of
object types, no way to prevent the value of the ptr
return new StringList(
CHR(13)||CHR(10), CHR(13), CHR(10) ) variable from being directly modified.
If you want to provide random access to a data source
Note that the order in which these are processed such as a LOB—and modifying the value of ptr would
matters, so a nested table wouldn’t be a good choice for essentially be making access random rather than from a
storing these. stream that’s “consumed” as it’s read—then you’d be
Three functions are declared as “not instantiable” better off defining a new object type hierarchy, and
in the InputChannel specification: read, readLn, and including a procedure to change the current position in
readRaw. These must be implemented in any subtypes the data source—like fseek() in a traditional file system.
declared beneath OutputChannel. Read and readRaw That procedure could then, for example, deal with the
return VARCHAR2 and RAW values of up to the length effects of a change in the current position on any data
specified in a parameter. Less will be returned if there’s that’s internally buffered.
not enough content in the channel to completely fill the There’s very little difference between
request. ReadLn returns a VARCHAR2 defined as the ClobInputChannel and BlobInputChannel. Source code
characters available for input until the first line for both is available in the accompanying Download.
termination character(s) are encountered or no more data
is available. The line termination characters are discarded. Transformation of data with OutputChannels
When there’s nothing left to return, the expected behavior and InputChannels
is for a NO_DATA_FOUND exception to be raised. Now that we have channels for both output and input,
Two other procedures are declared: open and close. we can implement various kinds of transformations.
Both are implemented as null procedures. They won’t As an example, I’ll look at encryption and decryption
have to be repeated in subtypes where open and close using the Oracle supplied package DBMS_
have no function, but may be overridden where necessary. OBFUSCATION_TOOLKIT.

InputChannels for CLOBs and BLOBs The DBMS_OBFUSCATION_TOOLKIT package


The first InputChannel subtype I’ll implement is The DBMS_OBFUSCATION_TOOLKIT implements two
ClobInputChannel, for reading from CLOBs. This will be versions of encryption and decryption. The basic methods
very straightforward, with the only complexity coming are desEncrypt and desDecrypt. These require an 8-byte
from the readLn function. The specification is described in key. More advanced encryption and decryption are
Listing 2. provided by the des3Encryption and des3Decryption
procedures, which use a 24-byte key. In this object type,
Listing 2. The ClobInputChannel declaration. I’ll use the basic encryption procedure; anyone interested
in switching to des3 will find it a trivial change.
CREATE OR REPLACE TYPE CLOBINPUTCHANNEL
UNDER INPUTCHANNEL
Both encryption and decryption are done in lengths
( that must be a multiple of 8. To maintain our ability to
the_clob clob,
ptr integer, view input and output channels abstractly, we’ll need to
constructor function ClobInputChannel( pClob in clob) do buffering when both writing and reading—this will
return self as result,
overriding member function read( allow us to freely read and write in whatever quantities
self in out nocopy ClobInputChannel,
pCount in number) return varchar2, we wish without regard to the 8-byte rule.
overriding member function readLn( Dealing with the end of the data being transformed is
self in out nocopy ClobInputChannel)
return varchar2, a bit more difficult. We have to be prepared to pad the
overriding member function readRaw(
self in out nocopy ClobInputChannel, very last of the output when finishing, making the total
pCount in number)return raw, length of the stream a multiple of 8. This is easily done; by
member procedure initClobInputChannel,
static procedure test overriding the close procedure, we can, in that procedure,
)INSTANTIABLE NOT FINAL
/ pad out any remaining fragment in the buffer. I’m going
8 Oracle Professional May 2004 www.pinnaclepublishing.com
to follow a rather questionable practice and simply pad if self.isActive then
vRaw := utl_raw.concat(self.buffer,pRaw);
any last fragment with CHR(0) characters until it has a while utl_raw.length(vRaw) >= self.blockSize loop
encryptWrite(
length of 8. Since we don’t know the total length of the utl_raw.substr(vRaw,1,self.blockSize));
data being encrypted when we start the process, we can’t if utl_raw.length(vRaw) > self.blockSize then
vRaw := utl_raw.substr(vRaw,self.blockSize + 1,
write out a header with the total length at the beginning utl_raw.length(vRaw)-self.blockSize);
else
of the stream. We could have the very last 8-byte section vRaw := utl_raw.cast_to_raw(null);
of the stream contain the number of bytes in the previous end if;
end loop;
8-byte section that are valid—that would require always self.buffer := vRaw;
end if;
reading ahead at least 24 bytes. To keep things relatively end;
simple, however, I’m going to ignore the whole problem,
pad with CHR(0) when necessary, and just read until WriteRaw begins by concatenating the pRaw
NO_DATA_FOUND is raised. parameter value to any existing content in the buffer.
Many of the encoding schemes you’ll encounter Substrings of size blockSize are then stripped off and
operate on specific lengths of data—base64 encoding, for passed to the internal-use procedure encryptWrite,
example, converts three characters to four, and in decoding which encrypts the raw string and passes to the child
converts four characters back to three. Encryption has a OutputChannel. Once done, any remaining fragment—
different wrinkle—it doesn’t modify the length of the whose length will be less than blockSize—is placed in
string being encrypted or decrypted, but it is important the buffer.
that the size of the string being encrypted match the size of The write procedure is a straight pass-through to
the string being decrypted. In other words, if you encrypt writeRaw—writeRaw (utl_raw.cast_to_Raw(pText))—
in 8-character groups, you can’t decrypt successfully in while writeLn isn’t implemented at all; the default
16-character groups. There’s an illustration of that in the behavior of write(pText||chr(13)||chr(10)) is satisfactory.
Download that accompanies this article. The close procedure must be used with this object
One last point before we get to Listing 3: While read/ type. When writes are issued, any data that’s less than
readLn and write/writeLn are available for both input blockSize is retained in the buffer. The close procedure
and output, internally they’re pass-throughs to readRaw checks the buffer and, if it’s not null, pads it with CHR(0)
and writeRaw. This is to minimize the possibility of characters until its length is a multiple of 8, and passes it
character-set conversions going on during the encryption along to the encryptWrite procedure. The length of the
and decryption processes. last section written may or may not be equal to blockSize,
but it will be a multiple of 8 characters.
Listing 3. DesEncryptOutputChannel. While I’m ignoring the issue of registering the size of
the data being encrypted, it would be possible to have the
CREATE OR REPLACE
TYPE DESENCRYPTOUTPUTCHANNEL UNDER OUTPUTCHANNEL object type track the length of the data as it’s written, and
( make the actual length available via a function call after
buffer raw(32767),
key raw(24), the close procedure is called. It’s already necessary to
child OutputChannel,
blockSize integer,
know the key and the blockSize to decrypt the data—it
constructor function DesEncryptOutputChannel( wouldn’t be a stretch to save the original length as well.
pKey in raw,
pChannel in out nocopy OutputChannel,
pBlockSize in number default 8 )
return self as result,
Decrypting data with DesDecryptInputChannel
overriding member procedure close, The decryption of data is, like most InputChannels,
overriding member procedure write(
pText in varchar2), slightly more complex than its corresponding
overriding member procedure writeRaw( pRaw in raw), OutputChannel, but the only really sticky part is the
member procedure encryptWrite(pRaw in raw)
) instantiable not final readLn procedure, which I’ll discuss in this section.
First, take a look at Listing 5, which illustrates
Listing 3 displays the specification of the DesDecryptInputChannel.
DesEncryptOutputChannel type. The constructor takes
the 8-character key as a raw, along with another
OutputChannel, to which the encrypted data is passed Listing 5. DesDecryptInputChannel.
along using writeRaw and the block size (the size in which CREATE OR REPLACE
data should be encrypted). Both the key and the block size TYPE DESDECRYPTINPUTCHANNEL
UNDER INPUTCHANNEL
must match when the data is decrypted (see Listing 4). (
key raw(8),
buffer raw(32767),
child inputChannel,
Listing 4. The writeRaw procedure. blockSize integer,
constructor function DesDecryptInputChannel(
overriding member procedure writeRaw(pRaw in raw) pKey in raw,
is pInputChannel InputChannel,
vRaw raw(32767); pBlockSize in number default 8)
begin return self as result,

www.pinnaclepublishing.com Oracle Professional May 2004 9


overriding member function read( end loop;
self in out nocopy DesDecryptInputChannel,
pCount in number) return varchar2, self.buffer := utl_raw.cast_to_raw(null);
overriding member function readLn( end if;
self in out nocopy DesDecryptInputChannel) else
return varchar2, vReturn := substr(vbuffer,1,
overriding member function readRaw( vTokenPos - 1);
self in out nocopy DesDecryptInputChannel, vBuffer := substr(vBuffer, vTokenPos +
pCount in number)return raw, length(vTokens(vTokenPtr)));
member procedure initDesDecryptInputChannel, self.buffer := utl_raw.cast_to_Raw(vBuffer);
member function readBlock( end if;
self in out nocopy DesDecryptInputChannel) return vReturn;
return raw, exception
static procedure test when no_data_found then
)INSTANTIABLE NOT FINAL if vBuffer is not null then
/ self.buffer := null;
return vBuffer;
else
The readLn procedure retrieves the list of line raise no_data_found;
end if;
terminators, initializes a variable to the current contents
of the buffer, and then goes into a loop, scanning for any end readLn;

of the available line terminators. If none are found, then


another block is read, decrypted, and concatenated to the Because line terminators may be of different sizes, we
variable, which is then scanned again. This continues need to watch for instances where a line terminator spans
until either a line terminator is found or the next block a block size—for example, if a CHR(13)||CHR(10) is
read would place the length of the variable beyond the used and is split between two adjacent blocks. If we did a
legal maximum in PL/SQL—32,767 characters (see simple test against that case, the CHR(13)||CHR(10) test
Listing 6). would fail, but the CHR(13) test would succeed, resulting
in an extra empty line being returned by the next
invocation of readLn. We guard against that by removing
Listing 6. A fragment of the readLn procedure. from the INSTR function a number of characters equal to
vTokens := self.getNewLineTokens(); one less than the maximum length of a line terminator.
-- get the maximum length of a line terminator, If block#1 contains ABCDEF<CR> and block#2
-- and substract 1.
select max(length(column_value)) - 1 contains <LF>GHIJKL, we need to ensure that the
into vMaxLength from table(vTokens);
vBuffer := utl_Raw.cast_to_varchar2(self.buffer); CHR(13) carriage return is properly combined with the
if vBuffer is null then CHR(10) line feed.
vBuffer := utl_raw.cast_to_Varchar2(
self.readBlock()); The only nonstandard function declared in the
end if;
while vTokenPtr = 0 loop object type specification displayed in Listing 5 is the
for i in vTokens.first .. vTokens.last loop member function readBlock, detailed in Listing 7.
vCurrentPos := instr(
substr(vbuffer,1,length(vBuffer) - vMaxLength), This reads data from the child InputChannel using
vTokens(i),1,1);
if vCurrentPos > 0 and vCurrentPos < vTokenPos child.readRaw(self.blockSize), decrypts the data, and
then returns the decrypted data to whatever procedure
vTokenPos := vCurrentPos;
vTokenPtr := i; invoked the function. It’s intended for internal use, and
end if;
end loop;
was written because I found myself repeating that bit of
if vTokenPtr = 0 then code in both the readRaw and readLn procedures.
-- nothing found yet
if length(vBuffer) + self.blockSize
< 32767 then
-- retrieve another block, descrypt it, add Listing 7. The readBlock function.
-- to buffer, and try again
begin
vBuffer := vBuffer member function readBlock(
|| utl_raw.cast_to_varchar2(self.readBlock()); self in out nocopy DesDecryptInputChannel) return raw
exception is
when no_Data_found then vReturn raw(32767);
exit; vRaw raw(32767);
end; begin
else vRaw := self.child.readRaw(self.blockSize);
-- can't find dbms_obfuscation_toolkit.desDecrypt(
exit; input=>vRaw,
end if; key=>self.key,
end if; decrypted_Data=>vReturn);
end loop; if vReturn is null then
if vTokenPtr = 0 then raise no_data_Found;
-- we failed. return what we have end if;
-- and set the buffer null. Get rid of any return vReturn;
-- chr(0) characters at the end end readBlock;
if vBuffer is null then
raise no_Data_Found;
else This is just three steps: Read in a raw string of length
vReturn := vbuffer;
while nvl(length(vReturn),0) > 0 and blockSize from the child InputChannel, decrypt it, and
substr(vReturn, length(vReturn),1) = chr(0) return the decrypted raw string. Note that while we may
loop
vReturn := substr(vReturn, 1, length(vReturn) - 1); expect the child InputChannel to raise a NO_DATA_

10 Oracle Professional May 2004 www.pinnaclepublishing.com


FOUND exception when out of data, we can’t be certain A7F4B7DC00287E40A3D89CB8137116
4FD40D112CC623
that that will happen—therefore, it’s raised here just to >/1005bd30_LnkdConstant<
>/10076b23_OraCustomDatumClosur<
be safe. >/10297c91_SAXAttrList<
>/103a2e73_DefaultEditorKitEndP<
><
Using the encryption and decryption object types no_data_found raised
A good way to illustrate how to use these object
PL/SQL procedure successfully completed.
types is to look at the test procedure declared in
DesDecryptInputChannel and shown in Listing 8. It’s a This is certainly not an adequate test of processes that
static procedure that does some modest testing of both are designed to make data completely unreadable, but it
object types. does serve to illustrate how the object types are used.
Setup for each type is a multi-step process since they
Listing 8. Testing encryption and decryption. require other object types from the same hierarchy to read
from and write to. If you wanted to always encrypt
static procedure test is
vEn DesEncryptOutputChannel; CLOBs, and store the encrypted results in BLOBs, then
vDe DesDecryptInputChannel; you could, for example, create your own subtypes that
vBlob blob;
vBl BlobOutputChannel; contained more of the setup in their constructor.
vBlIn BlobInputChannel;
vBlockSize integer := 2048;
vKey raw(8) := utl_raw.cast_to_raw('gmenchen');
cursor cur is
Connecting Inputs and Outputs
select object_name from all_objects I’ve already given examples of connecting Inputs to
where rownum < 5;
vPos integer; Inputs and Outputs to Outputs. Let’s take a quick look at
begin connecting an InputChannel to an OutputChannel—see
dbms_output.put_line('Original data');
for rec in cur loop Listing 9.
dbms_output.put_line(rec.object_name);
end loop;
dbms_lob.createTemporary(vBlob,true);
vBl := new blobOutputChannel(vBlob); Listing 9. ChannelBridge.
vEn := new DesEncryptOutputChannel(
vKey, vBl, vBlockSize);
for rec in cur loop CREATE OR REPLACE
vEn.writeLn(rec.object_name); TYPE CHANNELBRIDGE AS OBJECT
end loop; (
vEn.close(); -- remember to close! input InputChannel,
-- Now get the encrypted blob Output OutputChannel,
vBlob := constructor function ChannelBridge(
treat(vEn.child as BlobOutputChannel).getBlob(); pInput IN OUT NOCOPY InputChannel,
dbms_output.put_line('Dumping encrypted content'); pOutput IN OUT NOCOPY OutputChannel)
vPos := 1; return self as result,
while vPos <= dbms_lob.getlength(vBlob) loop member procedure open,
dbms_output.put_line( member procedure transfer( pBlockSize in integer
rawToHex(dbms_lob.substr(vBlob,15,vPos))); default 1024),
vPos := vPos + 15; member procedure close,
end loop; static procedure test
vBlin := new BlobInputchannel(vBlob); ) instantiable not final
vDe := new DesDecryptInputChannel(vKey,vBlin /
,vBlockSize);
begin
while true loop This object type makes maximum use of
dbms_output.put_line( '>'||vDe.readLn()||'<');
end loop; polymorphism, taking any InputChannel subtype, and
exception copying the contents to any OutputChannel subtype.
when no_Data_found then
dbms_output.put_line('no_data_found raised'); Normal use would simply be to make three procedure
end loop;
end;
calls—Open(), Transfer(), and Close()—and the only
reason to not have the complete conversion take place
The cursor here is limited to rownum < 5, but that’s in a single call to Transfer() is to leave the door open for
only to reduce the output displayed in the following conversion of InputChannels, such as Pipes, that may
block. Running it in SQL*Plus, we get this: not necessarily be terminated when all existing content
is consumed. The implementation, in the type body, is
SQL> set serveroutput on
SQL> begin shorter than the test procedure. The main work is done in
2 desDecryptInputChannel.Test;
3 end;
the transfer procedure, which simply reads from the input
4 / and writes to the output until a NO_DATA_FOUND
Original data
/1005bd30_LnkdConstant exception is raised.
/10076b23_OraCustomDatumClosur
/10297c91_SAXAttrList
/103a2e73_DefaultEditorKitEndP Conclusion
Dumping encrypted content
45FC19A72E35649B5321F2A45D9AF5 We’ve now completed input and output channels that
F26C2956E4A18185C6195127A934EB range from the trivial (output to dbms_output) to the
8513C1E9D3CB7B4028A48A495CE81C
7F7798DB85636F1B17678A66E50944 moderately complex (DES encryption and decryption). To
47CFBE65913AEF3E06451509249A48
6A9B77458BC6DA97A005EC818EBE56 Continues on page 16
www.pinnaclepublishing.com Oracle Professional May 2004 11
Oracle
Professional

Using the Oracle Trace Facility


Dan Hotka
The ability to collect all of the SQL statements from a information. Query V$SESSION and get the SID and
particular session, or even database-wide SQL statements, has SERIAL# for the session you want to trace. Why would
existed since at least Oracle7. The Oracle Trace Facility (OTF) is you want to do this? Perhaps a particular user is
the only way to capture SQL with useful timing information experiencing a particular issue and can easily re-create the
that comes free with the Oracle RDBMS. In this article, Dan issue. Starting trace on the user’s session will allow for all
Hotka reviews how to collect SQL statements using OTF, and of the SQL to be collected (a 10046 trace) for that session.
how to interpret the collection file using TKPROF. Notice the syntax in the second statement here that allows
you to collect extended 10046 trace information (bind

G
ENERALLY, you need a trace when you’re variables and wait event information):
debugging a particular issue, or trying to solve an
DBMS_SYSTEM.set_sql_trace_in_session
application performance problem. The trace file (<sid>, <serial#>, true);
contains all of the captured SQL, depending on the type DBMS_SUPPORT.start_trace_in_session
( <sid>, <serial#>, waits=>true, binds=>false);
of trace you initiate. Trace files can contain the SQL bind DBMS_SUPPORT.stop_trace_in_session(<sid>, <serial#>);
variables and Oracle wait event information (I covered
wait events in my article “Understanding Oracle Events” OTF also allows you to put an “identifier” into the
in the July 2001 issue). name, which makes finding your trace file much easier
Oracle creates raw trace files in the USER_DUMP_ (see Figure 1). I first learned to use Oracle Trace with
DEST location. Oracle always generates a trace file Oracle7, as this was the only way to see the row counts
upon startup and shutdown. You can initiate a trace for the intermediate explain plan steps at the time.
session at the database level, at the session level, Imagine several people creating trace files around the
programmatically out of a program, for another active same time—more than once, I tried to find my problem
session, and so forth. SQL in the wrong trace file! Use ALTER SESSION SET
Make sure that TIMED_STATISTICS = TRUE is set, TRACE_FILE_IDENTIFIER = 'xxx' to add some text
either for your session that you’re going to run traces to the trace file name. This identifier can be up to 60
from or at the system level. TIMED_STATISTICS doesn’t characters long.
have the extreme overhead it used to. I’d set this to true at To initiate SQL collections (10046 trace) with various
the database level. At the session level, you can enter options, you need to use the syntax shown in Listing 1.
ALTER SESSION SET TIMED_STATISTICS = TRUE for 10046 traces have several options (levels):
the duration of your trace. • 0 = No statistics
Issuing an ALTER SYSTEM SET SQL_TRACE = • 1 = Trace with parse, execute, and fetch statistics
TRUE creates a 10046 dump of all SQL being presented • 4 = Trace with option 1 with the SQL bind
to Oracle. Make sure you have plenty of room in your variable contents
USER_DUMP_DEST location for a dump like this. • 8 = Trace with option 1 with the Oracle wait
Optionally, you can ALTER SESSION SET SQL_TRACE = event information
TRUE at your session level. Then you’d run the SQL • 12 = Options 1, 4, and 8
on which you want to collect
information. ALTER SESSION SET
SQL_TRACE = FALSE turns the trace
facility off and closes the trace file.
A 10046 dump is a trace
containing information about SQL
statements. Later in this article, I’ll
identify several other kinds of traces.
A 10046 extended trace includes
bind variables and/or wait event
information.
Setting trace on for a different
session from yours takes a bit more Figure 1. Oracle trace files in USER_DUMP_DEST.
12 Oracle Professional May 2004 www.pinnaclepublishing.com
The syntax in Listing 1 shows how to set a 10046
enhanced SQL trace. Substitute your SQL for the “Hello
World” SQL example. This example will make a trace file
in the USER_DUMP_DEST with the identifier 'HOTKA' in
the file name and collect both bind variables and Oracle
wait events for the SQL presented in the session for which
this was initiated.

Listing 1. Enhanced 10046 SQL trace syntax.

Alter session set timed_statistics=true;


Alter session set tracefile_identifier='HOTKA';
Alter session set events '10046 trace
name context forever, level 12';
Select 'Hello World: Today is ' || sysdate from dual;
Alter session set events '10046 trace name context off'

Interpreting the trace output


Figure 2. TKPROF Help text.
TKPROF is the character-mode tool
that’s used to interpret the Oracle
trace file into a readable form.
TKPROF allows you to sort the
contents of the trace file by a number
of parameters. Figure 2 shows the
TKPROF command Help text. The
minimum information required to
look at a trace file is: TKPROF <trace
file name> <output file name>.
This will take the contents of the
trace file and format the SQL into the
order that it was collected into the
trace file. Figure 3. Trace file information in TKPROF output.
The EXPLAIN_PLAN option
isn’t required anymore. The SYS=NO
option is nice—it removes all data
dictionary SQL from the output. The
PRINT= option allows for only n
number of SQL statements to be put
into the output file. The SORT=
option is probably the most useful
option, as it allows you to order the
SQL by any of the options available.
Combining the SORT and PRINT
options allows you to see, for
example, the top 10 SQL statements
for the SQL causing the most read
activity during a fetch operation
(SORT=fchdsk PRINT=10).
I did a 10046 level 12 trace on
my session and ran some of my
course SQL. I wanted to see the SQL
consuming the most elapsed time
first. I used this TKPROF syntax
to produce an output file that I
then reviewed using Notepad:
DosPrompt> tkprof ora9r2_ora_
Level12.trc Level12.out sys=no
print=10 sort=fchela. This produced a Figure 4. Level 12 information in TKPROF output.

www.pinnaclepublishing.com Oracle Professional May 2004 13


file named Level12.out. Figure 3, Figure 4, and Figure 5
display this output.
The beginning of the file shows the trace file name
and any sort options used to organize the contents.
The level 12 trace in Figure 4 shows the SQL
statement and a variety of information about the parsing/
execution/data fetching of this query. Notice how the sort Figure 5. End of TKPROF output information.
options used from Figure 2 coordinate with this section of
the trace output. execute portion allows the user to select TKPROF execute
The next bit of information shows whether the SQL and output file display options. Notice that I’ve checked
was found in the library cache or not, the optimizer goal, View Output Files When Finished. On my Windows PC,
and the user id that developed the explain plan (parsing the output of TKPROF is displayed using Notepad.
user id). Using this interface and the Back and Finish buttons,
Next comes the explain plan. I ran SQL trace to see it’s easy to sort the contents of the trace file in a quick
the rows as they relate to each part of the explain plan. fashion that definitely aids in finding a SQL statement
Since this is a level 12 trace, if this SQL statement has that’s causing issues within the trace file.
bind variables, the contents of the variables will be
displayed. The level 12 trace also displays any wait event Other trace options
information. This information is very useful when trying The Oracle Trace Facility allows for more than just 10046
to diagnose problem SQL. For a full description of these SQL traces. Here’s a short list of some other available
various wait events, see my July 2001 article. Oracle traces:
The end of the trace output gives some useful • 10032—Sort Statistics
information, including the trace file name again, the sort • 10033—Sort Run Information
options used in TKPROF again, and the types of SQL • 10053—Shows All CBO Explain Plans Considered
found in the trace. (hope you have lots of disk space!)
TOAD, from Quest Software, contains a nice interface • 10079—SQL*Net Data Sent/Received
to the TKPROF utility (you can download a trial version • 10104—Hash Join Statistics
at www.quest.com). This wizard first appeared in TOAD • 10241—Remote SQL Execution Information
version 7.4 and makes using TKPROF very easy. Start
the wizard from the Tools menu bar and select Tkprof More information
Interface as in Figure 6. Cary Millsap and Jeff Holt wrote the book Optimizing
Click on the Files button Oracle Performance for O’Reilly (ISBN: 0-596-00527-x). I
to display available Oracle reviewed this book for O’Reilly and found it to be a great
trace files in the Oracle User resource for anyone working with the Oracle Trace
Dump Destination (see Figure
7). This button also allows
you to change directories to
other locations where you
may have stored trace files.
Figure 8 shows how easy
it is to select the command-
line TKPROF sort options.
Simply check the sort options
you want. Not selecting any
items processes the trace
file in the order that the SQL
statements appear in the
trace file.
Figure 9 displays the final
wizard screen, with the
rest of the TKPROF options
such as the number of SQL
statements to display, an
option to re-explain the SQL,
and other features such as Figure 6. Tkprof Interface Figure 7. Finding Oracle trace files using the TOAD Tkprof
consolidating like SQL. The access using TOAD. Interface.

14 Oracle Professional May 2004 www.pinnaclepublishing.com


Facility. Cary and Jeff document finding a variety of plan/results/bind variables and Oracle wait events for
issues in the Oracle database using trace files and their any particular SQL statement. ▲
collection of Perl scripts. I found their insights about
various details, particularly the 10046 level 12 trace file Dan Hotka is a training specialist who has more than 25 years of
experience in the computer industry and more than 20 years of
contents, to be incredibly useful.
experience with Oracle products. He’s an internationally recognized
Oracle expert with Oracle experience dating back to the Oracle v4.0 days.
Summary His current book, TOAD Handbook (from SAMS), is now available on
Oracle Trace Facility has been available for quite a while www.Amazon.com. He’s also the author of Oracle9i Development By
and is a useful, sometimes critical diagnostic tool for the Example and Oracle8i from Scratch (both from Que) and has co-authored
Oracle RDBMS. This article illustrated the ease of using five other popular books. He’s frequently published in Oracle trade
the Oracle Trace Facility. OTF allows you to collect SQL journals and regularly speaks at Oracle conferences and user groups
for any given session, letting you view exact time/explain around the world. www.DanHotka.com, dhotka@earthlink.net.

Figure 8. TKPROF sort options. Figure 9. Final TOAD Tkprof Interface wizard step.

Don’t miss another issue! Subscribe now and save!


Subscribe to Oracle Professional today and receive a special one-year introductory rate:
Just $179* for 12 issues (that’s $20 off the regular rate)

NAME ❑ Check enclosed (payable to Pinnacle Publishing)


❑ Purchase order (in U.S. and Canada only); mail or fax copy
COMPANY
❑ Bill me later
❑ Credit card: __ VISA __MasterCard __American Express
ADDRESS
CARD NUMBER EXP. DATE

CITY STATE/PROVINCE ZIP/POSTAL CODE


SIGNATURE (REQUIRED FOR CARD ORDERS)

COUNTRY IF OTHER THAN U.S.


Detach and return to:
Pinnacle Publishing ▲ 316 N. Michigan Ave. ▲ Chicago, IL 60601
E-MAIL Or fax to 312-960-4106

* Outside the U.S. add $30. Orders payable in


405INS
PHONE (IN CASE WE HAVE A QUESTION ABOUT YOUR ORDER) U.S. funds drawn on a U.S. or Canadian bank.

Pinnacle, A Division of Lawrence Ragan Communications, Inc. ▲ 800-493-4867 x.4209 or 312-960-4100 ▲ Fax 312-960-4106

www.pinnaclepublishing.com Oracle Professional May 2004 15


Oracle10g PL/SQL... Steven Feuerstein is considered one of the world’s leading experts
on the Oracle PL/SQL language, having written nine books on
Continued from page 6 PL/SQL, including Oracle PL/SQL Programming and Oracle PL/SQL
• The PL/SQL team can now focus its attention on Best Practices (all from O’Reilly & Associates). Steven has been
developing software since 1980 and serves as a Senior Technology
some core architectural aspects of PL/SQL. In fact,
Advisor to Quest Software. His current projects include Swyg
they’ve constructed an entirely new infrastructure
(www.Swyg.com) and the Refuser Solidarity Network
that now allows them to roll out new (to PL/SQL,
(www.refusersolidarity.net), which supports the Israeli military
that is, while they’re standard and long-available in refuser movement. steven@stevenfeuerstein.com.
other languages) important capabilities, such as
compile-time warnings. Chip Dawes has been building and maintaining systems on relational
databases since 1988 and with Oracle since 1990. He’s currently a
The bottom line? It’s a fine time to be a PL/SQL consultant with D&D Technologies, a Chicago consultancy.
developer. More on Oracle10g to come! ▲ chip@ddtechnologies.com.

Input Channels... Thanks to polymorphism and inheritance, each new


subtype then becomes interchangeable with the other
Continued from page 11 types already declared under the InputChannel and
OutputChannel types. ▲
the degree currently possible in Oracle, I’ve taken
advantage of the object-oriented features of object types to 405MENCHEN.TXT at www.pinnaclepublishing.com
provide a simple interface that hides the complexity (or
simplicity) of the actual I/O being performed. There are Gary Menchen is a senior programmer analyst at Dartmouth College in
numerous other subtypes that can be defined as well. Hanover, New Hampshire. Gary.E.Menchen@Dartmouth.edu.

May 2004 Downloads


• 405MENCHEN.TXT—Source code to accompany Gary Menchen’s article, “Object Types and Inheritance, Part 2: Input Channels
and Data Transformation.”

For access to current and archive content and source code, log in at www.pinnaclepublishing.com.

Editor: Garry Chan (gchan@procaseconsulting.com) Oracle Professional (ISSN 1525-1756)


Contributing Editor: Bryan Boulton is published monthly (12 times per year) by:
CEO & Publisher: Mark Ragan
Pinnacle Publishing
Group Publisher: Michael King A Division of Lawrence Ragan Communications, Inc.
Executive Editor: Farion Grove 316 N. Michigan Ave., Suite 300
Chicago, IL 60601
Questions?
POSTMASTER: Send address changes to Lawrence Ragan Communications, Inc., 316
N. Michigan Ave., Suite 300, Chicago, IL 60601.
Customer Service:
Phone: 800-493-4867 x.4209 or 312-960-4100 Copyright © 2004 by Lawrence Ragan Communications, Inc. All rights reserved. No part
Fax: 312-960-4106 of this periodical may be used or reproduced in any fashion whatsoever (except in the
case of brief quotations embodied in critical articles and reviews) without the prior
Email: PinPub@Ragan.com
written consent of Lawrence Ragan Communications, Inc. Printed in the United States
of America.
Advertising: RogerS@Ragan.com
Oracle, Oracle 8i, Oracle 9i, PL/SQL, and SQL*Plus are trademarks or registered trademarks of
Editorial: FarionG@Ragan.com Oracle Corporation. Other brand and product names are trademarks or registered trademarks
of their respective holders. Oracle Professional is an independent publication not affiliated
Pinnacle Web Site: www.pinnaclepublishing.com with Oracle Corporation. Oracle Corporation is not responsible in any way for the editorial
policy or other contents of the publication.

Subscription rates This publication is intended as a general guide. It covers a highly technical and complex
subject and should not be used for making decisions concerning specific products or
applications. This publication is sold as is, without warranty of any kind, either express or
United States: One year (12 issues): $199; two years (24 issues): $348 implied, respecting the contents of this publication, including but not limited to implied
Other:* One year: $229; two years: $408 warranties for the publication, performance, quality, merchantability, or fitness for any particular
purpose. Lawrence Ragan Communications, Inc., shall not be liable to the purchaser or any
Single issue rate: other person or entity with respect to any liability, loss, or damage caused or alleged to be
caused directly or indirectly by this publication. Articles published in Oracle Professional
$27.50 ($32.50 outside United States)* reflect the views of their authors; they may or may not reflect the view of Lawrence Ragan
Communications, Inc. Inclusion of advertising inserts does not constitute an endorsement by
* Funds must be in U.S. currency. Lawrence Ragan Communications, Inc., or Oracle Professional.

16 Oracle Professional May 2004 www.pinnaclepublishing.com

You might also like