You are on page 1of 280

All non-technical views expressed are those of Steven Feuerstein and do

not necessarily (and not likely!) reflect those of Quest or the TCOUG.

The Brave New World
of Oracle PL/SQL

"Gotta Know, Gotta Use"
New Features of
Oracle8i, Oracle9i and Oracle10g PL/SQL

Steven Feuerstein
steven@stevenfeuerstein.co
Copyright 2000-2005 Steven Feuerstein - Page 1 m
The New Topography of a Powerful
Language

 New/enhanced data structures and data types
– Collections, object types, TIMESTAMP,
INTERVAL, XMLType
 Enhancements to the SQL-PL/SQL interface
– Native Dynamic SQL, bulk processing, record-
based DML
 Miscellaneous - but not leftovers
– DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, Java
from PL/SQL, UTL_FILE enhancements, CASE,
row level security
Copyright 2000-2005 Steven Feuerstein - Page 2
"All" about Steven Feuerstein
www.stevenfeuerstein.com

 Bachelor's degree in mathematics (1980), with three
computer 101 classes to my name (a self-taught
programmer).
 Five years with Oracle Corporation (1987 - 1992), with
too much time spent helping salespeople sell. Life is
too short...
 Author/co-author of nine texts on PL/SQL, most
notably Oracle PL/SQL Programming.
 I live in Chicago with one wife (Veva), two sons (Chris
and Eli), and three cats (Sister, Moshe and Mica).
Copyright 2000-2005 Steven Feuerstein - Page 3
Ten Years of Writing on
the Oracle PL/SQL Language

Copyright 2000-2005 Steven Feuerstein - Page 4
Software Used in Training

 You can download all my training materials and
demonstration scripts from:
– http://oracleplsqlprogramming.com/resources.h
tml
 Toad and/or SQL Navigator: make sure you've got a top-
notch IDE, otherwise you are wasting lots of time.
 Ounit and utPLSQL, software for unit testing of PL/SQL code
at www.ounit.com.
 Mastoid Mind and Set: have fun while keeping
your brain tuned up.
 Qnxo, active mentoring software: a repository of
reusable and templated code, www.qnxo.com.
Copyright 2000-2005 Steven Feuerstein - Page 5
plsql_ides.txt
About Qnxo

 Qnxo is a searchable, customizable repository for
reusable code and templates.
– Starter set: "PL/SQL by Feuerstein"
– You can create our own toolboxes and libraries.
 Qnxo is a flexible code generator that helps you
avoid writing tedious, repetitive code.
 Qnxo is an error manager for PL/SQL-based
applications.
Copyright 2000-2005 Steven Feuerstein - Page 6
Qnxo is....

 NOT an integrated development environment,
aka IDE, aka editor, for PL/SQL programming.
– It complements Toad, SQL Navigator, PL/SQL
Developer, etc.
 NOT free. Qnxo is sold onButa we
subscription basis
will raffle off a one (you
year sub in class!
are paying for content).
– There is a trial version and there will be a free
version in the future (design platform minus
content).
 NOT needed in order to benefit from this class.
– Qnxo contains a repository of examples.
– 2000-2005
Copyright The Qnxo backend
Steven Feuerstein - Page 7 reflects my latest (and, I
Some PL/SQL Fundamentals

 The STANDARD package and how to be a
PL/SQL sleuth
 The PL/SQL run-time memory architecture
 Quick reminders about packages
– Overloading: use it to improve the usability
of your code.
– Packaged data
– The initialization section
– Packages or stand-alone programs?
Copyright 2000-2005 Steven Feuerstein - Page 8
The STANDARD Package and the
Rdbms/Admin Directory

 Much of what we consider to be the base
PL/SQL language is defined in the STANDARD
package.
– One of two "default" packages; the other is
DBMS_STANDARD.
 You can view the contents of the STANDARD
package (and other "supplied" packages by
visiting the appropriate variation of:
$ORACLE_HOME/Rdbms/Admin
Copyright 2000-2005 Steven Feuerstein - Page 9
PL/SQL in Shared Memory

System Global Area (SGA) of RDBMS
Instance
Shared Pool Library cache
Shared SQL
Select * Update emp
Reserved Pool
Pre-parsed from emp Set sal=...

Large Pool calc_totals show_emps upd_salaries

emp_rec emp%rowtype; emp_rec emp%rowtype;
Session 1 tot_tab tottabtype; tot_tab tottabtype;

Session 1 memory Session 2 memory Session 2
(PGA/UGA) (PGA/UGA)
mysess.pkg
Copyright 2000-2005 Steven Feuerstein - Page 10 Sess2.sql
Code and Data in Shared Memory

 PL/SQL is an interpretative language. The source
code is “partially compiled” into an intermediate form
(“p-code”).
– The p-code is loaded into the shared pool when any
element of that code (package or stand-alone
program) is referenced.
 The partially-compiled code is shared among all
users who have EXECUTE authority on the
program/package.
 Each user (Oracle session) has its own copy of any
data structures defined within the program/package.
– Distinct sets of in-memory data (not shared among
different users) are stored in the PGA.
Copyright 2000-2005 Steven Feuerstein - Page 11
Overloading in packages:
key usability technique

 Overloading: two or more programs with the
same name, but different signature.
– You can overload in the declaration section
of any PL/SQL block, including the package myproc
body (most common).
myfunc
 Overloading is a critical feature when building
comprehensive programmatic interfaces myproc
(APIs) or components using packages.
– If you want others to use your code, you
need to make that code as smart and as
easy to use as possible.
– Overloading transfers theCompare: "need to know"
DBMS_OUTPUT and p
Copyright 2000-2005 Steven Feuerstein - Page 12
from the user to the overloaded program.
How Overloading Works

 For two or more modules to be overloaded,
the compiler must be able to distinguish
between the two calls at compile-time.
– Another name for overloading is "static
polymorphism."
 There are two different "compile times":
– 1. When you compile the package or block
containing the overloaded code.
– 2. When you compile programs that use
the overloaded code.
Copyright 2000-2005 Steven Feuerstein - Page 13
How Overloading Works, continued

 Distinguishing characteristics:
– The formal parameters of overloaded modules
must differ in number, order or datatype family
(CHAR vs. VARCHAR2 is not different enough).
– The programs are of different types: procedure
and function.
 Undistinguishing characteristics:
– Functions differ only in their RETURN datatype.
– Arguments differ only in their mode (IN, OUT, IN
OUT).
– Their formal parameters differ only in datatype
and Steven
Copyright 2000-2005 theFeuerstein
datatypes - Page 14 are in the same family.
Examples of Invalid Overloadings
PACKAGE too_similar
IS
Parameter
PROCEDURE calc (reg_in IN CHAR); data types
PROCEDURE calc (reg_in IN VARCHAR2); cause conflict.
END too_many_cals;

which one? too_similar.calc ('123');

PACKAGE only_returns
IS Only difference
FUNCTION func1 (val IN VARCHAR2) RETURN DATE; is function
FUNCTION func1 (val IN VARCHAR2) RETURN VARCHAR2; RETURN type.
END only_returns;

which one? DBMS_OUTPUT.PUT_LINE (only_returns.func1 (v_value));

PACKAGE param_modes
IS Only difference
PROCEDURE proc1 (val IN VARCHAR2); is parameter
PROCEDURE proc1 (val IN OUT VARCHAR2); mode.
END param_modes;

which one? param_modes.proc1 (v_value);
Copyright 2000-2005 Steven Feuerstein - Page 15
Quiz! Nuances of Overloading

 Is this a valid overloading? Will it compile? How
can I use it?
CREATE OR REPLACE PACKAGE sales
IS
PROCEDURE calc_total (zone_in IN VARCHAR2);

PROCEDURE calc_total (reg_in IN VARCHAR2);
END sales;

BEGIN
sales.calc_total ('NORTHWEST');
?
sales.calc_total ('ZONE2');
END;
sales.pkg

Copyright 2000-2005 Steven Feuerstein - Page 16
Package Data: Useful and Sticky

 The scope of a package is your session, and any
data defined at the "package level" also has
session scope.
– If defined in the package specification, any
program can directly read/write the data.
– Ideal for program-specific caching.
 General best practice: hide your package data in
the body so that you can control access to it.
 Use the SERIALLY_REUSABLE pragma to move
data to SGA and have memory released after each
usage.
thisuser.pkg
thisuser.tst
serial.sql
Copyright 2000-2005 Steven Feuerstein - Page 17
Package Initialization Structure
 The initialization section:
– Is defined after and outside PACKAGE BODY pkg
IS
of any programs in the PROCEDURE proc IS
package. BEGIN
END;
– Is not required. In fact, most
packages you build won't FUNCTION func RETURN
BEGIN
have one. END;
– Can have its own exception BEGIN
...initialize...
handling section. END pkg;

 Useful for:
– Performing complex setting BEGIN after/outside
of default or initial values. of any program
defined in the pkg.
– Setting up package data init.pkg
which does not change for init.tst
datemgr.pkg
the
Copyright duration
2000-2005 of - a
Steven Feuerstein Pagesession.
18
Packages vs. Stand-alone programs

 General recommendation: use packages
instead of stand-alone programs.
– Better way to organize code.
– Can hide implementation and reduce need
to recompile programs using the package.
 Other considerations....
– Entire package loaded when any single
program is called.
– Central packages can become a
"bottleneck" when changes are needed.
recompile.sql
Copyright 2000-2005 Steven Feuerstein - Page 19
New, Improved and Useful Data Structures

 Collections grow ever more powerful
 Object types finally start to remotely
resemble classes
 More intelligent date-time-interval handling
 Native XML datatype and operations

Copyright 2000-2005 Steven Feuerstein - Page 20
PL/SQL Collections

 Collections are single-dimensioned lists
of information, similar to 3GL arrays.
 They are an invaluable data structure; all
PL/SQL developers should be familiar
with them -- and use them a lot.
 They take some getting used to,
especially when you want to leverage the
latest features, such as multi-level
collections.
array (15) array (15, 77)
Copyright 2000-2005 Steven Feuerstein - Page 21
Why Use Collections?

 Maintain any kind of list of related information for use
in your programs.

 Emulate bi-directional cursors, which are not yet
supported in PL/SQL
 Cache data in program memory for faster access.
 Avoid mutating table errors in database triggers.
 Mirror complex data structures from database
directly in program data structures.
 Emulate multi-dimensional arrays in PL/SQL.

Copyright 2000-2005 Steven Feuerstein - Page 22
Three Types of Collections

 Associative arrays - previously known as "index-by
tables" (V8) and "PL/SQL tables" (V7)
– Available in PL/SQL only; preferred structures for
developers as they are the easiest to use.
 Nested tables
– Can be defined in PL/SQL and SQL. Use to store large
amounts of persistent data in the column of a table, use
with table functions.
– Required for some features, such as table functions
 Variable arrays
– Can be defined in PL/SQL and SQL; useful for defining
small lists in columns of relational tables. assoc_array_example.sql
nested_table_example.sql
Copyright 2000-2005 Steven Feuerstein - Page 23 varray_example.sql
Getting to Know Collections

 Defining collection types and collections
 Navigating collection contents
 Manipulating collections inside SQL
 New features in Oracle9i
– Multi-level collections
– Indexing by strings
 New features in Oracle10g
– Nested table operators

Copyright 2000-2005 Steven Feuerstein - Page 24
Defining Collections

 First, you define the TYPE of the collection.
– For associative arrays, this can only occur in a PL/SQL
declaration section. Best option: package specification.
– For nested tables and VARRAYs, you can define the TYPE
in the database with a CREATE statement, or in a PL/SQL
declaration section.
 Then you declare one or more instances, actual
collections, from the TYPE..
CREATE OR REPLACE PACKAGE coll_types
IS
TYPE integer_aat IS TABLE OF INTEGER INDEX BY PLS_INTEGER;
TYPE integer_nt IS TABLE OF INTEGER;
TYPE integer_vat IS VARRAY(10) OF INTEGER;
...
END coll_types;
Copyright 2000-2005 Steven Feuerstein - Page 25
Initializing Collections

 Before you can use a collection, it must be initialized.
– Nested tables and VARRAYs are atomically null. You must
initialize them explicitly with a constructor. Associative arrays
are initialized automatically.
DECLARE
TYPE numbers_t IS VARRAY (10) OF NUMBER; TYPE defined in
salaries numbers_t := numbers_t (100, 200, 300); PL/SQL
BEGIN

CREATE TYPE numbers_t IS VARRAY (10) OF NUMBER;
/
DECLARE -- Initialize the collection.
TYPE defined in
salaries numbers_t := numbers_t (100, 200, 300); the database
BEGIN

CREATE TABLE employee_denorm ( Collection used
employee_id INTEGER,
in a table
salary_history numbers_t);
Copyright 2000-2005 Steven Feuerstein - Page 26
More about Associative Arrays

 They are unbounded, practically speaking.
– Valid row numbers range: -2,147,483,647 to
2,147,483,647. You will not actually create tables this
large; they will use up all available memory.
– Instead, this range allows you to employ the row number
as an intelligent key, such as the primary key or unique
index value, because AAs are:
 Sparse
– Data does not have to be stored in consecutive rows, as
is required in traditional 3GL arrays.
 Try to read a row that doesn't exist and Oracle
raises NO_DATA_FOUND. emplu.*

Copyright 2000-2005 Steven Feuerstein - Page 27
Wide Variety of Collection Methods
 Obtain information about the collection
– COUNT returns number of rows currently defined in
collection.
– EXISTS returns TRUE if the specified row is defined.
– FIRST/LAST return lowest/highest numbers of defined rows.
– NEXT/PRIOR return the closest defined row after/before the
specified row.
– LIMIT tells you the max. number of elements allowed in a
VARRAY.
 Modify the contents of the collection
– DELETE deletes one or more rows from the index-by table.
– EXTEND adds rows to a nested table or VARRAY.
– TRIM removes rows from a VARRAY.
Copyright 2000-2005 Steven Feuerstein - Page 28
The DELETE Method

 You can delete one or more rows from a collection
using DELETE:

BEGIN
-- Delete all rows
myCollection.DELETE;

-- Delete one (the last) row
myCollection.DELETE (myCollection.LAST);

-- Delete a range of rows
myCollection.DELETE (1400, 17255);
END;

DELETE releases memory, but you may also want to call
DBMS_SESSION.FREE_UNUSED_USER_MEMORY.
Copyright 2000-2005 Steven Feuerstein - Page 29
Navigating Through Collections

 Use FIRST and NEXT to move from beginning to end.
 Use LAST and PRIOR to move from end to beginning.

rowind PLS_INTEGER := birthdays.FIRST;
-- REVERSE: birthdays.LAST
BEGIN
WHILE (rowind IS NOT NULL)
LOOP
DBMS_OUTPUT.PUT_LINE (birthdays(rowind).best_present);

rowind := birthdays.NEXT (rowind);
-- REVERSE: birthdays.PRIOR
END LOOP;
END;

plsqlloops.sp
notworking.sql
notworking_NT.sql
Copyright 2000-2005 Steven Feuerstein - Page 30 notworking_VA.sql
Using Collections Inside SQL

 You can apply SQL operations to the contents of
nested tables and VARRAYs with these operators:
– CAST - Maps a collection of one type to a collection
of another type
– MULTISET - Maps a database table to a collection
– TABLE - Maps a collection to a database table
 Index-by tables are programmatic constructs only.
– You cannot make a direct reference to an index-by
table in SQL.
– Instead, do so indirectly with a PL/SQL function.

Copyright 2000-2005 Steven Feuerstein - Page 31
Using Collections inside SQL
SELECT column_value
FROM TABLE (SELECT children FROM db_family
WHERE surname = 'BOLZ');

db_family column_value
Barbara Anne
surname children Gary Richard Barbara Anne
BOLZ Lisa Marie Gary Richard
Lisa Marie
BOND
Eric Thomas
Max Richard
Barbara Anne
Gary Richard
UPDATE TABLE Lisa Nadezhka
(SELECT children FROM db_family
WHERE SURNAME = 'BOLZ)
SET column_value = 'Lisa Nadezhka' db_family
WHERE column_value = 'Lisa Marie');
surname children
BOLZ
Copyright 2000-2005 Steven Feuerstein - Page 32 ...
Using the TABLE and CAST Operators

 Use CAST to convert a collection from one type to another,
TABLE to convert a TYPE into a database table.
– Useful when you would like to apply SQL operations against
a PL/SQL collection (ie, one not stored in a database table).
DECLARE
nyc_devolution cutbacks_for_taxcuts :=
cutbacks_for_taxcuts ('Stop rat extermination programs',
'Fire building inspectors',
'Close public hospitals');
BEGIN
DBMS_OUTPUT.PUT_LINE (
'How to Make the NYC Rich Much, Much Richer:');
FOR rec IN (SELECT COLUMN_VALUE ohmy
FROM TABLE (CAST (nyc_devolution AS cutbacks_for_taxcuts)))
LOOP
DBMS_OUTPUT.PUT_LINE (rec.ohmy);
END LOOP; cast.sql
END;
Copyright 2000-2005 Steven Feuerstein - Page 33
Using the MULTISET Operator

 MULTISET is the inverse of TABLE, converting a set of
table, view, query) into a VARRAY or nested table.
– Use MULTISET to emulate or transform relational joins into
collections, with potential client-server performance impact.
DECLARE
CURSOR bird_curs IS
SELECT b.genus, b.species,
CAST(MULTISET(SELECT bh.country FROM bird_habitats bh
WHERE bh.genus = b.genus
AND bh.species = b.species)
AS country_tab_t)
FROM birds b; Retrieves all detail
bird_row bird_curs%ROWTYPE; information for the
BEGIN master in one trip.
OPEN bird_curs;
FETCH bird_curs into bird_row;
END;
Copyright 2000-2005 Steven Feuerstein - Page 34 multiset.sql
Referencing Associative Arrays inside SQL

 You can't directly reference an index-by table's
contents inside SQL.
– Call functions that retrieve data from hidden array.

CREATE OR REPLACE PACKAGE ibtab IS
FUNCTION rowval (indx IN PLS_INTEGER) RETURN DATE; Make accessible
PRAGMA RESTRICT_REFERENCES (rowval, WNPS, WNDS); in SQL for
END; Oracle8 and
below.
CREATE OR REPLACE PACKAGE BODY ibtab IS
TYPE date_tab IS TABLE OF DATE INDEX BY BINARY_INTEGER;
hiredates date_tab;

FUNCTION rowval (indx IN PLS_INTEGER) RETURN DATE
IS BEGIN
RETURN hiredates (indx);
END;
END; ibtab_in_sql.sql
Copyright 2000-2005 Steven Feuerstein - Page 35
Putting Collections to Work

 Let's take a closer look at some of the ways
you can use collections in PL/SQL programs.

 Enhancing date-string conversion
 Bi-directional cursor emulation
datemgr*.pkg
 Data caching dates.sql
bidir.*
 Mutating table error resolution emplu.*

Copyright 2000-2005 Steven Feuerstein - Page 36
The Mutating Table Problem

 Database triggers can be associated with both the
DML statement as a whole and individual rows
affected by that statement.
UPDATE emp SET sal = 1000
Statement level
UPDATE row 1

Row level
UPDATE row N

 Row level triggers cannot query from or
change the contents of the table to which it Note: in Oracle8i, you
can use autonomous
is attached; it is "mutating". transactions to relax
restrictions
 So what are you supposed to do when a associated with
row-level operation needs to "touch" that queries.
table?
mutating.sql
Copyright 2000-2005 Steven Feuerstein - Page 37
A Solution Based on
Associative Arrays Tables

 Since you cannot perform the processing desired
in the row-level trigger, you need to defer the
action until you get to the statement level.
 If you are going to defer the work, you have to
remember what you needed to do.
– an index-by table is an ideal repository for this
reminder list. Work List
Writes to list (PL/SQL Table)
1st row trigger fires

Writes to list
Nth row trigger fires

Process data
in the list.
Statement Trigger
Copyright 2000-2005 Steven Feuerstein - Page 38
An Example:
Ranking Salespeople
 A table holds the rankings based on the amount of annual sales
of salespeople within a department.
– As the sales amount is updated in this table, the rankings
must also change to show the new standings for that
department only.
Department ID Salesperson ID Sales Amount Rank
1055 64333 74055.88 3
1055 65709 144533.91 1
1055 65706 109000.25 2
1047 70904 65011.25 6
 "Deferred work" is not only necessary, but preferable.
– By storing the salesperson’s department ids as they
change, we then know which departments to re-rank.
– We might update more than one department within a
statement so we must be able to retain multiple
Copyright 2000-2005 Steven Feuerstein - Page 39
department numbers.
Trigger Logic Required
Row Level Trigger
Doesn't fire unless
CREATE OR REPLACE TRIGGER Rank_Sales_Rtrg the sales_amt
AFTER insert OR update OF sales_amt is actually changed.
ON rank_sales FOR EACH ROW
WHEN (OLD.sales_amt != NEW.sales_amt)
BEGIN
rank.add_dept (:new.dept_id);
END;

Statement Level Trigger
CREATE OR REPLACE TRIGGER Rank_Sales_Strg
AFTER INSERT OR UPDATE OR DELETE
ON rank_sales
BEGIN All details of the
rank.rank_depts; ranking are hidden
END; in the package body.

ranking.pkg
Copyright 2000-2005 Steven Feuerstein - Page 40
The Ranking Package
PACKAGE rank
IS
PROCEDURE add_dept (dept_id_in IN INTEGER);

PROCEDURE rank_depts;
END rank;

PACKAGE BODY rank Table holds indicator
IS that department
in_process BOOLEAN := FALSE; needs re-ranking.

TYPE dept_tabtype IS TABLE OF BOOLEAN INDEX BY BINARY_INTEGER;
dept_tab dept_tabtype;

PROCEDURE add_dept (dept_id_in IN INTEGER) IS
BEGIN
IF NOT in_process
THEN Create row to indicate
dept_tab (dept_id_in) := TRUE; department to be ranked.
END IF;
END add_dept
Copyright 2000-2005 Steven Feuerstein - Page 41
The Ranking Package, Continued

PROCEDURE rank_depts
IS
v_deptid PLS_INTEGER := dept_tab.FIRST;
BEGIN

IF NOT in_process Avoid recursive
THEN execution of logic
in_process := TRUE;
LOOP
EXIT WHEN v_deptid IS NULL;
perform_ranking (v_deptid);
v_deptid := dept_tab.NEXT (v_deptid);
END LOOP;
END IF; Row number is
department number.
in_process := FALSE;
dept_tab.DELETE;
END rank_dept;
Clean up for
END rank; next time.

Copyright 2000-2005 Steven Feuerstein - Page 42
Oracle9i Multi-level Collections

 Oracle9i allows you to create collections of
collections, or collections of records that
contain collections, or...
 Applies to all three types of collections.
 Two scenarios to be aware of:
– Named collection columns
– Anonymous collection columns

Copyright 2000-2005 Steven Feuerstein - Page 43
Collections with Named,
Oracle9i
Multi-level Collections

 When a collection is based on a record or
object that in turn contains a collection, that
collection has a name.
CREATE TYPE vet_visit_t IS OBJECT (
visit_date DATE,
reason VARCHAR2 (100));
/
CREATE TYPE vet_visits_t IS TABLE OF vet_visit_t
/
CREATE TYPE pet_t IS OBJECT ( Collection nested inside
tag_no INTEGER, object type
NAME VARCHAR2 (60),
petcare vet_visits_t,
MEMBER FUNCTION set_tag_no (new_tag_no IN INTEGER)
RETURN pet_t);
/
Copyright 2000-2005 Steven Feuerstein - Page 44 multilevel_collections.sql Continued...
Collections with Named,
Oracle9i
Multi-level Collections, continued

DECLARE
TYPE bunch_of_pets_t IS TABLE OF pet_t INDEX BY BINARY_INTEGER;
my_pets bunch_of_pets_t;
BEGIN
my_pets (1) := Outer collection
pet_t (
100, 'Mercury',
vet_visits_t (
vet_visit_t ( Inner collection
'01-Jan-2001', 'Clip wings'),
vet_visit_t (
'01-Apr-2002', 'Check cholesterol')
)
);
DBMS_OUTPUT.put_line (my_pets (1).petcare (2).reason);
END;

Copyright 2000-2005 Steven Feuerstein - Page 45
Oracle9i Anonymous Collection Columns

 If your nested collections do not rely on "intermediate"
records or objects, you simply string together index
subscripts.
– To demonstrate this syntax, let's take a look at how to
emulate a three-dimensional array using nested
collections.
 First, we cannot directly reference or populate an
individual cell, as one would do in a 3GL.
– Instead we have to build an interface between the
underlying arrays and the user of the "three
Can'tdimensional
do this... array." Have to do something like this instead...
BEGIN BEGIN
gps_info (1, 45, 605) := gps_info (605) (45) (1) :=
l_value; l_value;
Copyright 2000-2005 Steven Feuerstein - Page 46
Oracle9i Multi-dimensional array emulation

CREATE OR REPLACE PACKAGE multdim
IS
TYPE dim1_t IS TABLE OF VARCHAR2 (32767)
INDEX BY BINARY_INTEGER;
Three levels of
collections TYPE dim2_t IS TABLE OF dim1_t INDEX BY BINARY_INTEGER;

TYPE dim3_t IS TABLE OF dim2_t INDEX BY BINARY_INTEGER;

PROCEDURE setcell (
array_in IN OUT dim3_t,
dim1_in PLS_INTEGER,
Set a cell value dim2_in PLS_INTEGER,
dim3_in PLS_INTEGER,
value_in IN VARCHAR2
);

FUNCTION getcell (
array_in IN dim3_t,
dim1_in PLS_INTEGER,
Get a cell value dim2_in PLS_INTEGER,
dim3_in PLS_INTEGER multdim.*
) RETURN VARCHAR2; multdim2.*
END multdim; gen_multcoll.sp
Copyright 2000-2005 Steven Feuerstein - Page 47
Oracle9i Multi-dimensional array emulation

CREATE OR REPLACE PACKAGE BODY multdim
IS
PROCEDURE setcell (
array_in IN OUT dim3_t,
dim1_in PLS_INTEGER,
dim2_in PLS_INTEGER,
dim3_in PLS_INTEGER,
value_in IN VARCHAR2
) IS
BEGIN
array_in(dim3_in )(dim2_in )(dim1_in) := value_in;
END;

FUNCTION getcell (
array_in IN dim3_t,
As close as you dim1_in PLS_INTEGER, multdim.*
can get... dim2_in PLS_INTEGER, multdim2.*
dim3_in PLS_INTEGER) gen_multcoll.sp
RETURN VARCHAR2
IS
BEGIN
RETURN array_in(dim3_in )(dim2_in )(dim1_in);
END;
Copyright 2000-2005 Steven Feuerstein - Page 48
Oracle9i Release 2 New Indexing Capabilities

 You can now define the index on your
associative array to be:
– Any sub-type derived from BINARY_INTEGER
– VARCHAR2(n), where n is between 1 and 32767
– %TYPE against a database column that is
consistent with the above rules
 This means that you can now index on string
values!

Copyright 2000-2005 Steven Feuerstein - Page 49
Examples of New
Oracle9i Release 2
TYPE Variants
 All of the following are now valid TYPE declarations in
Oracle9i Release 2
– You cannot use %TYPE against an INTEGER column,
because INTEGER is not a subtype of BINARY_INTEGER.

DECLARE
TYPE array_t1 IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
TYPE array_t2 IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
TYPE array_t3 IS TABLE OF NUMBER
INDEX BY POSITIVE;
TYPE array_t4 IS TABLE OF NUMBER
INDEX BY NATURAL;
TYPE array_t5 IS TABLE OF NUMBER
INDEX BY VARCHAR2(64);
TYPE array_t6 IS TABLE OF NUMBER
INDEX BY VARCHAR2(32767);
TYPE array_t7 IS TABLE OF
INDEX BY NUMBER
employee.last_name%TYPE;
TYPE array_t8 IS TABLE OF NUMBER INDEX BY
types_pkg.subtype_t;

Copyright 2000-2005 Steven Feuerstein - Page 50
Working with VARCHAR2-Indexed
Oracle9i Release 2
Collections
DECLARE
TYPE population_type IS TABLE OF NUMBER INDEX BY VARCHAR2(64);

country_population population_type;
continent_population population_type;

howmany NUMBER;
BEGIN
country_population('Greenland') := 100000;
country_population('Iceland') := 750000;
assoc_array*.sql
assoc_array_perf.tst
howmany := country_population('Greenland');

continent_population('Australia') := 30000000;
END;

 Specifying a row via a string takes some getting
used to, but if offers some very powerful advantages.
Copyright 2000-2005 Steven Feuerstein - Page 51
Oracle9i Release 2
Rapid Access to Data
Via String Keys
 One of the most powerful applications of this
features is to construct very fast pathways to static
data from within PL/SQL programs.
– If you are repeatedly querying the same data from the
database, why not cache it in your PGA inside
collections?
 Emulate the various indexing mechanisms (primary
key, unique indexes) with collections.

Comparison of performance
Demonstration package: Generate a caching package:
of different approaches:
assoc_array5.sql genaa.sql
vocab*.*

Copyright 2000-2005 Steven Feuerstein - Page 52
The power of multi-level and string-
based indexes
 Careful -- and creative! -- application of this
functionality can greatly simplify the code you need to
write to handle complex requirements.
 Let's step through a demonstration of this capability
using Codecheck as an example.

Codecheck
Codecheck: package
overloadings
A QA package
naming_conventions
for PL/SQL
bad_datatypes

Copyright 2000-2005 Steven Feuerstein - Page 53
The problem of
ambiguous package overloadings

 Oddly and sadly, it is possible to compile
overloadings which are not usable.
– You see an obvious example below, but there are
many more subtle circumstances, usually
involving defaulted parameters.
 So I will build a program to identify such ambiguous
overloadings. But how can I do this?
PACKAGE salespkg
IS BEGIN
PROCEDURE calc_total (

?
salespkg.calc_total (
dept_in IN VARCHAR2); 'ABC');
PROCEDURE calc_total ( END;
dept_in IN CHAR); /
END salespkg;

Copyright 2000-2005 Steven Feuerstein - Page 54
ALL_ARGUMENTS to the rescue!

 Parsing is too complicated for me, but the
ALL_ARGUMENTS data dictionary view
contains information about all the arguments
of all the procedures and functions to which I
have access. That sounds pretty good!
 As usual, Oracle offers us a whole lot of
pleasure, mixed with a little bit of pain.
– The organization of data in
ALL_ARGUMENTS is a bit bizarre, plus it is
incomplete, necessitating the use also of
DBMS_DESCRIBE.DESCRIBE_COLUMNS.
Copyright 2000-2005 Steven Feuerstein - Page 55
First Inclination:
Same Old, Same Old
 All right then, I will grab all the information from
ALL_ARGUMENTS and dump it into a collection
based on that view! Very easy...
CREATE OR REPLACE PROCEDURE get_all_arguments (
package_in IN VARCHAR2)
IS
TYPE all_arguments_tt IS TABLE OF all_arguments%ROWTYPE Emulate the
INDEX BY BINARY_INTEGER; view.
l_arguments all_arguments_tt;
BEGIN
FOR rec IN (
SELECT * FROM all_arguments
WHERE owner = USER AND package_name = package_in)
LOOP Load it up!
l_arguments (SQL%ROWCOUNT) := rec;
END LOOP;
END;
Copyright 2000-2005 Steven Feuerstein - Page 56
Then what? Write lots of code to
interpret the contents...

 Which programs are overloaded? Where
does one overloading end and another start?
l_last_program all_arguments.object_name%TYPE;
l_is_new_program BOOLEAN := FALSE;
l_last_overload PLS_INTEGER := -1; IF l_arguments (indx).overload
BEGIN != l_last_overload
FOR indx IN l_arguments.FIRST .. OR l_last_overload = -1
l_arguments.LAST THEN
LOOP IF l_is_new_program
IF l_arguments (indx).object_name != THEN
l_last_program do_first_overloading_stuff;
OR l_last_program IS NULL ELSE
THEN do_new_overloading_stuff;
l_last_program := END IF;
l_arguments (indx).object_name; END IF;
l_is_new_program := TRUE; END LOOP;
do_new_program_stuff; END;
END IF;
...
Copyright 2000-2005 Steven Feuerstein - Page 57
Discovery: there is a natural hierarchy
to ALL_ARGUMENTS data!
Program name

RUN_TEST
Overloading
SHOW_RESULTS
Overloading 1
RESET_FLAGS
Overloading 2 Breakout
Argument
 Each program has zero or Breakout 1
Argument 1
more overloadings, each
overloading has N Argument 2
Breakout 1
arguments, and each Argument 3

argument can have Argument 4
Breakout 2

multiple "breakouts" (my Argument 5
Breakout 3

term - applies to non-
scalar parameters, such as
records or object types).
Copyright 2000-2005 Steven Feuerstein - Page 58
What if I reflect this hierarchy in
a collection of collections?

 Have to build from the bottom up:
1. Set of rows from TYPE breakouts_t IS TABLE OF all_arguments%ROWTYPE
ALL_ARGUMENTS INDEX BY BINARY_INTEGER;

2. All the "breakout" info TYPE arguments_t IS TABLE OF breakouts_t
for a single argument INDEX BY BINARY_INTEGER;

3. All the argument info TYPE overloadings_t IS TABLE OF arguments_t
for a single overloading INDEX BY BINARY_INTEGER;

TYPE programs_t IS TABLE OF overloadings_t
4. All the overloadings for INDEX BY all_arguments.object_name%type;
a distinct program name

String-based index
Copyright 2000-2005 Steven Feuerstein - Page 59
Then I can populate it very easily

 Assigning a single record to the "lowest" level also
defines each of the upper levels.
 Notice the automatic "SELECT DISTINCT" on program
name that results!
FOR rec IN (SELECT * FROM all_arguments)
LOOP I can still do the
l_arguments (NVL (l_arguments.LAST, 0) + 1) typical sequential
:= rec; load.

l_programs
(rec.object_name)
But I will now also
(NVL (rec.overload, 0))
add the multi-level
(rec.position)
load in single
(rec.data_level) := rec;
assignment
END LOOP;

Copyright 2000-2005 Steven Feuerstein - Page 60
A closer look at the multi-level
assignment
all programs in package
distinct program name in package

g_programs Nth overloading for program (record)
(c_program)
(c_overload) field containing all parameters for overloading
.PARAMETERS
(c_position) Nth parameter in the program's parameter list
.toplevel

:= g_all_arguments ( field containing a single row from
argindx_in); ALL_ARGUMENTS (more or less) with the
top-level parameter information

the record that is
assigned as the top-
level parameter

Copyright 2000-2005 Steven Feuerstein - Page 61
And then I can "query" the contents
with a minimum of code
Is the TOP_SALES
program overloaded? l_programs ('TOP_SALES').COUNT > 1

Is the 2nd overloading
of TOP_SALES a l_programs ('TOP_SALES') (2).EXISTS (0)
function?

What is the datatype
of the RETURN clause l_programs ('TOP_SALES') (2)(0)(0).datatype
of the 2nd overloading
of TOP_SALES?

And, of course, I know the beginning and end points of
each program, overloading, and argument. I just use the
FIRST and LAST methods on those collections!

Copyright 2000-2005 Steven Feuerstein - Page 62
Some best practices for these complex
structures

 As you can see, you can easily and rapidly
arrive at completely unreadable and un-
maintainable code.
 What' s a developer to do?
– Hide complexity -- and all data structures --
behind small modules.
– Functions to retrieve contents and
procedures to set contents.
cc_smartargs.pkb:
cc_smartargs.next_overloading
cc_smartargs.add_new_parameter
Copyright 2000-2005 Steven Feuerstein - Page 63
Nested Tables unveil their
Oracle10g
MULTISET-edness

 Oracle10g introduces high-level set
operations on nested tables (only).
– Nested tables are “multisets,” meaning
that theoretically there is no order to their
elements. This makes set operations of
critical importance for manipulating
nested tables. .
 You can now…
– Check for equality and inequality
– Obtain UNION, INTERSECT and MINUS of
two Steven
Copyright 2000-2005 NTsFeuerstein - Page 64
Oracle10g Check for equality and inequality

 Just use the basic operators….
DECLARE
TYPE clientele IS TABLE OF VARCHAR2 (64);
group1 clientele := clientele ('Customer 1', 'Customer 2');
group2 clientele := clientele ('Customer 1', 'Customer 3');
group3 clientele := clientele ('Customer 3', 'Customer 1');
BEGIN
IF group1 = group2 THEN
DBMS_OUTPUT.put_line ('Group 1 = Group 2');
ELSE
DBMS_OUTPUT.put_line ('Group 1 != Group 2');
END IF;

IF group2 != group3 THEN
DBMS_OUTPUT.put_line ('Group 2 != Group 3');
ELSE
DBMS_OUTPUT.put_line ('Group 2 = Group 3');
END IF;
END; 10g_compare.sql
Copyright 2000-2005 Steven Feuerstein - Page 65 10g_compare_old.sql
Oracle10g UNION, INTERSECT, MINUS

 Straightforward, with the MULTISET keyword.
BEGIN
our_favorites := my_favorites MULTISET UNION dad_favorites;
show_favorites ('MINE then DAD', our_favorites);

our_favorites := dad_favorites MULTISET UNION my_favorites;
show_favorites ('DAD then MINE', our_favorites);

our_favorites := my_favorites MULTISET UNION DISTINCT dad_favorites;
show_favorites ('MINE then DAD with DISTINCT', our_favorites);

our_favorites := my_favorites MULTISET INTERSECT dad_favorites;
show_favorites ('IN COMMON', our_favorites);

our_favorites := dad_favorites MULTISET EXCEPT my_favorites;
show_favorites ('ONLY DAD''S', our_favorites);
END;
10g_setops.sql
10g_string_nt.sql
Copyright 2000-2005 Steven Feuerstein - Page 66 10g_favorites.sql
10g*union*.sql
Oracle10g Distinct sets of values

 Use the SET operator to work with distinct values, and
determine if you have a set of distinct values.
DECLARE
keep_it_simple strings_nt := strings_nt ();
BEGIN
keep_it_simple := SET (favorites_pkg.my_favorites);

favorites_pkg.show_favorites ('FULL SET', favorites_pkg.my_favorites);

p.l (favorites_pkg.my_favorites IS A SET, 'My favorites distinct?');
p.l (favorites_pkg.my_favorites IS NOT A SET, 'My favorites NOT distinct?');

favorites_pkg.show_favorites (
'DISTINCT SET', keep_it_simple);

p.l (keep_it_simple IS A SET, 'Keep_it_simple distinct?');
p.l (keep_it_simple IS NOT A SET, 'Keep_it_simple NOT distinct?');

END;

10g_set.sql
Copyright 2000-2005 Steven Feuerstein - Page 67 10g_favorites.pkg
Oracle10g Determining subsets of data

 Use the SUBMULTISET operator to determine if a
nested table contains only elements that are in
another nested table.
BEGIN
p.l (favorites_pkg.my_favorites
SUBMULTISET OF favorites_pkg.eli_favorites
, 'Father follows son?');

p.l (favorites_pkg.eli_favorites
SUBMULTISET OF favorites_pkg.my_favorites
, 'Son follows father?');

p.l (favorites_pkg.my_favorites
NOT SUBMULTISET OF favorites_pkg.eli_favorites
, 'Father doesn''t follow son?');

p.l (favorites_pkg.eli_favorites
NOT SUBMULTISET OF favorites_pkg.my_favorites
, 'Son doesn''t follow father?');
END; 10g_submultiset.sql
Copyright 2000-2005 Steven Feuerstein - Page 68 10g_favorites.pkg
The Wonderful World of
Oracle Object Types

A New Frontier:

Object Types and
Object-oriented Development
in PL/SQL

Copyright 2000-2005 Steven Feuerstein - Page 69
Which best describes your
relationship with Object Types?

 I love 'em and use 'em all the time.
 They scare me. I'll stick with good, old-
fashioned relational tables.
 I am comfortable with defining and using
object types, but not in production.
 We use object types and have
incorporated them into our production
applications.

Copyright 2000-2005 Steven Feuerstein - Page 70
Object Types in Oracle

 Object types were first introduced into the
Oracle8 RDBMS (the "object-relational" model).
– Oracle uses object types in many of its new features
(e.g., Oracle AQ, the XML datatype).
– Few development shops work with object types.
 The implementation is weak.
– Not really object oriented.
– Advantages are not persuasive to developers and
DBAs with relational and procedural backgrounds.
– Oracle9i support for inheritance may well change this
situation..
Copyright 2000-2005 Steven Feuerstein - Page 71
Some Terminology

 Object type - Oracle's version of a class; a structure that
combines data with programs (a blend of a table and a
package).
 Object type instance - Oracle's version of an object
 Attribute - a characteristic or "column" of the object type.
 Method - a program that manipulate an object type instance.
 Supertype - a type that is closer in the hierarchy to the root
 Subtype - a type that has a supertype.
 Inheritance - the ability of a subtype to inherit attributes and
methods from a supertype.
 Overriding - replacing functionality of a supertype with new
functionality in a subtype.
Copyright 2000-2005 Steven Feuerstein - Page 72
A Very Simple Object Type Example

CREATE TYPE food_t AS OBJECT (
name VARCHAR2(100),
food_group VARCHAR2 (100), Attributes
grown_in VARCHAR2 (100)
);

 The food type contains three attributes and no
methods or programs.
 It is very similar to a CREATE TABLE statement,
but it does not create a "container" for data. Rather
it is a "template" for data or instances.

Copyright 2000-2005 Steven Feuerstein - Page 73
Working with Simple Objects

DECLARE
Create a new my_favorite_vegetable food_t
object with a := food_t ('Brussel Sprouts',
constructor. 'VEGETABLE',
'Farm,Greenhouse,Backyard');
BEGIN
Read an attribute DBMS_OUTPUT.put_line (
my_favorite_vegetable.name);
value
my_favorite_vegetable.food_group :=
'SATISFACTION';
Modify an
attribute value IF INSTR (
my_favorite_vegetable.grown_in,
'yard') > 0
THEN
Pass an object order_seeds (my_favorite_vegetable);
END IF;
as a parameter
END;
objtype.sql
Copyright 2000-2005 Steven Feuerstein - Page 74
Another Object Type Example

 The timer object CREATE TYPE tmr_t AS OBJECT (

calculates startTime INTEGER,
endTime INTEGER,
elapsed time. repetitions INTEGER, Attributes
name VARCHAR2(2000),
 It consists of four
MEMBER PROCEDURE go ,
attributes and MEMBER PROCEDURE stop (
five methods. show_timing IN BOOLEAN := TRUE),
MEMBER FUNCTION timing RETURN INTEGER,
MEMBER PROCEDURE reset (
name IN VARCHAR2 := NULL),
STATIC FUNCTION make ( Methods
name IN VARCHAR2,
repetitions IN INTEGER := 1)
RETURN tmr_t
);
tmr.ot
Copyright 2000-2005 Steven Feuerstein - Page 75
About Constructor Functions

CREATE TYPE tmr_t AS OBJECT (
 Each object type ...

comes with its own CONSTRUCTOR FUNCTION tmr_t (
SELF IN OUT tmr_t,
constructor function. name IN VARCHAR2,
repetitions IN INTEGER
Oracle9i
Release 2
– You must supply a )
RETURN SELF AS RESULT
value for each );
CREATE OR REPLACE TYPE BODY tmr_t
attribute. AS
 Oracle9i Release 2 CONSTRUCTOR FUNCTION tmr_t (
SELF IN OUT tmr_t,
allows you design name IN VARCHAR2,
repetitions IN INTEGER
user-defined )
RETURN SELF AS RESULT
constructors, so you IS
BEGIN
can hide attributes SELf.repetitions := repetitions;

and perform SELF.name := name;
RETURN;
additional init. tasks. END;
END;
Copyright 2000-2005 Steven Feuerstein - Page 76
Using the Timer Object

DECLARE
Declare timer with once_tmr tmr_t := NEW tmr_t (
default constructor NULL, NULL, 10000, 'Packaged Function');

myonce_tmr tmr_t := tmr_t.make (
Declare timer with 'Packaged Constant',10000);
"pseudo-constructor"
every_tmr tmr_t := NEW tmr_t (
'USER Function', 10000);
Declare timer with user-
defined constructor
BEGIN
once_tmr.go ();
FOR indx IN 1 .. count_in
Invoke object type LOOP
methods using dot v := thisuser.name;
notation. END LOOP;
once_tmr.stop ();

thisuser.tst
Copyright 2000-2005 Steven Feuerstein - Page 77
Oracle9iSupport for inheritance in object types

 You can now define a hierarchy of subtypes of
object types.
 A subtype contains all the attributes and
methods of the parent type (or supertype).
 The subtypes can also contain additional
attributes and additional methods, and can
override methods from the supertype.
 You decide if an object type is INSTANTIABLE
or is FINAL (cannot be extended to a subtype).

Copyright 2000-2005 Steven Feuerstein - Page 78
Oracle9i
Type Hierarchy Example

supertype/"wider" subtype/"narrower"

Citizen

Person
Employee Hourly Worker
Management
"root" type Salaried
Corporation Non-
Worker
Management

An employee is one sort of person. An hourly
worker is one sort of employee. An employee is
person.ot always a person, but a person may not be an
employee.
Copyright 2000-2005 Steven Feuerstein - Page 79
Why Bother with Hierarchy?

 Define shared functionality once at the
"widest" level and it is automatically available
at all narrower points in the hierarchy.
– A very powerful approach to code reuse.
 Easily customize or override functionality for
specific subtypes.
– You get the best of both worlds: rely on the
supertype-standard, and selectively over-ride that
functionality as needed.
Copyright 2000-2005 Steven Feuerstein - Page 80
Oracle9i Let's Build a Type Hierarchy

 We have a three level
"root",
supertype food hierarchy:
of dessert
– food is the root type.
– desserts are a type of food
subtype of
food,
supertype
dessert – cakes are a type of dessert.
of cake
 We will make cake the most
subtype
specialized type of food
cake
of dessert
allowed in the hierarchy.
food.ot

Copyright 2000-2005 Steven Feuerstein - Page 81
Creating a Simple
Object Type Hierarchy
CREATE TYPE food_t AS OBJECT (
name VARCHAR2(100),
 NOT FINAL
food_group VARCHAR2 (100),
grown_in VARCHAR2 (100))
indicates that this
NOT FINAL; type can be a
CREATE TYPE dessert_t UNDER food_t ( supertype.
contains_chocolate CHAR(1),
year_created NUMBER(4))  UNDER denotes
NOT FINAL;
that this type is a
CREATE TYPE cake_t UNDER dessert_t (
diameter NUMBER,
subtype.
inscription VARCHAR2(200));

food.ot

An object instantiated from food_t has three attributes. A dessert
object has five attributes. A cake has seven.
Copyright 2000-2005 Steven Feuerstein - Page 82
Substitutability of
Object Types

"Any object of type cake is also a dessert,
is also a food."
 A supertype is substitutable if one of its
subtypes can substitute or stand in for it in
a slot (a variable, column, etc.) whose
declared type is the supertype.
 Oracle supports object type substitution in
columns of relational tables, attributes of
object types and elements in collections.
Copyright 2000-2005 Steven Feuerstein - Page 83
Populate an Object Table

 Create a table of objects of type food (root type).
CREATE TABLE sustenance OF food_t;

 Populate it with objects at different levels in hierarchy.
DECLARE Use of constructor to
my_favorite_vegetables food_t := initialize a variable
food_t ('Brussel Sprouts', 'VEGETABLE', 'farm' );
BEGIN
INSERT INTO sustenance VALUES (my_favorite_vegetables);

INSERT INTO sustenance
VALUES (dessert_t ('Jello', 'PROTEIN', 'bowl', 'N', 1887 ) );

INSERT INTO sustenance Substitution of subtypes
VALUES (cake_t (
'Marzepan Delight', 'CARBOHYDRATE', 'bakery',
'N', 1634, 8, 'Happy Birthday!' ) ); food.ot
END;
Copyright 2000-2005 Steven Feuerstein - Page 84
Objects in a Collection

 Create a table of objects of type food (root type).

DECLARE
TYPE foodstuffs_nt IS TABLE OF food_t; Declare a nested
table
fridge_contents foodstuffs_nt
:= foodstuffs_nt (
food_t ('Eggs benedict', 'PROTEIN', 'Farm'),
dessert_t ('Strawberries and cream', 'FRUIT',
'Backyard', 'N', 2001),
cake_t (
'Chocolate Supreme', 'CARBOHYDATE', 'Kitchen',
'Y', 2001, 8, 'Happy Birthday, Veva'
) Insert three different
); objects in the collection,
BEGIN each of a different type.
...

Copyright 2000-2005 Steven Feuerstein - Page 85
Accessing Attributes in Substituted
Types

 You can substitute a subtype in a supertype
column or attribute, but subtype-specific attributes
and methods are by default not visible.
SQL> DECLARE
4 mmm_good food_t :=
5 dessert_t ('Super Brownie', 'CARBOHYDRATE',
6 'my oven', 'Y', 1994);
7 BEGIN
8 DBMS_OUTPUT.PUT_LINE (mmm_good.contains_chocolate);
9 END;
10 /
DBMS_OUTPUT.PUT_LINE (mmm_good.contains_chocolate);
*
ERROR at line 8:
PLS-00302: component 'CONTAINS_CHOCOLATE' must be declared

Copyright 2000-2005 Steven Feuerstein - Page 86
Converting Between Types

 Use the TREAT operator to "treat" or convert
a wider type into a narrower subtype:
CREATE TABLE books (title VARCHAR2(30),
author Person_ot /* substitutable */);

BEGIN
INSERT INTO books
VALUES ('Oracle PL/SQL Programming',
citizen_ot ('HUMAN','Steven Feuerstein',
180, '23-SEP-1958', 'USA', 'Independent'

));
END; treat2.sql
/
SELECT author.nation FROM Books; -- WILL NOT WORK!

CopyrightSELECT TREAT(author
2000-2005 Steven Feuerstein - PageAS
87 citizen_ot).nation FROM Books; - WORKS!
Use TREAT to Identify Constrained
Types

/* Show all the meals in which a main course is a dessert */
SELECT *
FROM meal
WHERE TREAT (main_course AS dessert_t) IS NOT NULL;

/* Will fail, since main_course is of food_t type */
SELECT main_course.contains_chocolate
FROM meal
WHERE TREAT (main_course AS dessert_t) IS NOT NULL;

/* Now works, since I am treating main_course as a dessert */
SELECT TREAT (main_course AS dessert_t).contains_chocolate
FROM meal
WHERE TREAT (main_course AS dessert_t) IS NOT NULL;

/* Set to NULL any desserts that are not cakes... */ treat.sql
UPDATE meal
SET dessert = TREAT (dessert AS cake_t);
Copyright 2000-2005 Steven Feuerstein - Page 88
Example: NOT SUBSTITUTABLE

 The appetizer must be of type food_t;
desserts are not acceptable.
SQL> BEGIN
2 INSERT INTO meal VALUES (
3 SYSDATE + 1,
4 dessert_t ('Strawberries and cream',
5 'FRUIT', 'Backyard', 'N', 2001),
6 food_t ('Eggs benedict', 'PROTEIN', 'Farm'),
7 cake_t ('Apple Pie', 'FRUIT',
8 'Baker''s Square', 'N', 2001, 8, NULL));
9 END;
10 /
BEGIN
* notsubst.sql
ERROR at line 1:
ORA-00932: inconsistent datatypes

Copyright 2000-2005 Steven Feuerstein - Page 89
Constraining Substitutability to
a Specific Type

 Suppose I want a column of desserts to
contain only cakes?
CREATE TABLE meal (
served_on DATE,
appetizer food_t, Unconstrained
main_course food_t, non-substitutability
dessert dessert_t
)
COLUMN appetizer NOT SUBSTITUTABLE AT ALL LEVELS,
Constrain to a
COLUMN dessert IS OF (ONLY cake_t)
single subtype
;

You can only constrain to a
notsubst.sql
single type, not a list of types.
Copyright 2000-2005 Steven Feuerstein - Page 90
Turning Off Substitutability

 Oracle provides syntax to turn off substitutability on
either an entire type or specific attributes of a type.
CREATE TABLE brunches OF food_t
NOT SUBSTITUTABLE AT ALL LEVELS;
At the table level
CREATE TABLE meal (
served_on DATE,
appetizer food_t,
main_course food_t, For a single column
dessert dessert_t
)
COLUMN appetizer NOT SUBSTITUTABLE AT ALL LEVELS;

Copyright 2000-2005 Steven Feuerstein - Page 91
Creating "Template" Types

CREATE TYPE Address_t AS
OBJECT (
street VARCHAR2(1000),
city VARCHAR2(30),
...
)
NOT INSTANTIABLE
NOT FINAL;

 Some object types should only be used as
supertypes for other types; they are so general, you
would not actually create and manipulate instances
from those types.
– Oracle allows you to define object types as NOT
INSTANTIABLE, as shown above.
Copyright 2000-2005 Steven Feuerstein - Page 92
Creating and Overriding Methods

 Most real-world object types will have both
attributes and methods, programs that
perform operations on attributes.
 With inheritance, you can:
– inherit supertype methods
– override or replace supertype methods with
subtype implementations
– add completely new methods

Copyright 2000-2005 Steven Feuerstein - Page 93
Overriding to Provide Specificity for
Subtypes
CREATE OR REPLACE TYPE BODY dessert_t
 Different IS
OVERRIDING MEMBER
calculations for FUNCTION price RETURN NUMBER IS
mult NUMBER := 1;

desserts and BEGIN
IF SELF.contains_chocolate = 'Y'

cakes. THEN mult := 2; END IF;
IF SELF.year_created < 1900
THEN mult := mult + 0.5; END IF;
RETURN (10.00 * mult );
END;
CREATE OR REPLACE TYPE BODY cake_t
END;
IS
OVERRIDING MEMBER FUNCTION price RETURN NUMBER
IS
BEGIN Generic dessert prices are
RETURN ( determined by chocolate
5.00 content and age. Cake prices
+ 0.25 * (LENGTH (SELF.inscription)) are driven by inscription
+ 0.50 * diameter); length and size..
END;
END;

Copyright 2000-2005 Steven Feuerstein - Page 94 food2.ot
Disallowing Overrides

CREATE TYPE Person_t AS
OBJECT (
ssn NUMBER,
name VARCHAR2(30),
address VARCHAR2(100),
FINAL MEMBER PROCEDURE showInfo)
NOT FINAL;

 If you do not want a subtype to modify the
behavior of a supertype method, declare it to
be FINAL.
– You can do this even in object types that are
NOT FINAL.

Copyright 2000-2005 Steven Feuerstein - Page 95
Requiring Overrides to Methods

CREATE TYPE food_t AS OBJECT ( food2.ot
name VARCHAR2(100),
food_group VARCHAR2 (100),
grown_in VARCHAR2 (100), If any member is NOT
NOT INSTANTIABLE MEMBER INSTANTIABLE, then the
FUNCTION price RETURN NUMBER entire type must be
) declared the same way.
NOT FINAL
NOT INSTANTIABLE;

 Suppose the implementation of a method changes with
each subtype in the hierarchy and the supertype's
implementation really is irrelevant.
 Declare the method to be NOT INSTANTIABLE and then
(a) you do not have to provide an implementation of the
method and (b) all subtypes must provide an
implementation.
Copyright 2000-2005 Steven Feuerstein - Page 96
Quiz: What is "Dynamic Polymorphism"?

 A new kind of diet regimen.
 A form of sex therapy practiced in
Luxembourg.
 A computer language's ability to anticipate
the requirements of a system and generate
matching code.
 A computer language's ability to choose at
run-time among different forms of the same
program.

Copyright 2000-2005 Steven Feuerstein - Page 97
About Polymorphism

 The ability to choose from multiple methods of the
same name and execute the appropriate method.
– Static polymorphism: the decision about which method to
execute is made at the time the code is compiled. Static
polymorphism is also known as overloading, and is
supported in declaration sections of PL/SQL blocks.
– Dynamic polymorphism: the decision about which method to
execute is made at the time the code is executed, at run-
time. This is also known as "dynamic method dispatch", and
is available for the first time in PL/SQL with support for
object type inheritance.

Copyright 2000-2005 Steven Feuerstein - Page 98
Exploring Dynamic Polymorphism

 The food and CREATE TYPE food_t AS OBJECT (
...attributes...
dessert types MEMBER FUNCTION price RETURN NUMBER
) NOT FINAL;
each have a price
method, but cake CREATE TYPE dessert_t UNDER food_t (
...attributes...
does not. OVERRIDING MEMBER FUNCTION price
RETURN NUMBER
 It simply inherits ) NOT FINAL)
;
the dessert
CREATE TYPE cake_t UNDER dessert_t (
method. ...attributes...
-- No price method of its own.
);

Copyright 2000-2005 Steven Feuerstein - Page 99
A Visual Representation

 The root price
food Price function is over-
the "original" ridden in the
dessert subtype.
 The cake
dessert Price subtype now
An override
simply inherits
its price
calculation from
cake
Inherited its dessert
calculation supertype.
Copyright 2000-2005 Steven Feuerstein - Page 100
Dynamically Choosing
the Right Method

DECLARE
TYPE foodstuffs_nt IS TABLE OF food_t;
A collection of foods is
fridge foodstuffs_nt populated with three
:= foodstuffs_nt ( different object types.
food_t ('Eggs benedict', ...),
dessert_t ('Strawberries and cream', ...),
cake_t ('Chocolate Supreme', ...));
BEGIN
FOR indx IN food3.ot
fridge.FIRST ..
fridge.LAST
LOOP food4.ot
DBMS_OUTPUT.put_line ( dynpoly_overhead.tst
'Price of ' || fridge (indx).NAME ||
' = ' ||
fridge (indx).price); The price invocation is
END LOOP; resolved at run-time, and
END; not necessarily as the
food_t.price method.
Copyright 2000-2005 Steven Feuerstein - Page 101
Object Types Summary

 They are finally becoming robust enough to
be useful
 Object types are being used extensively by
Oracle itself.
– This fact makes more confident of the future,
performance and capabilities of object types.
 Get familiar with the syntax so that you can
work with object types with confidence.

Copyright 2000-2005 Steven Feuerstein - Page 102
New and Enhanced Datatypes in
Oracle9i

 TIMESTAMP and INTERVAL
 XMLType
 The ANY* "generic" types

Copyright 2000-2005 Steven Feuerstein - Page 103
Timestamps & Intervals

 TIMESTAMP
– Extends the DATE datatype, offering a much
higher (and variable) precision of seconds.
 INTERVAL
– Store and manipulate intervals of years and
months.
– DAY TO SECOND: represent the precise
difference between two datetime values.
– YEAR TO MONTH: calculate the difference
between two datetime values, where the only
significant portions are the year and month.
Copyright 2000-2005 Steven Feuerstein - Page 104
Timestamp Precision

DECLARE
checkout TIMESTAMP(3);
BEGIN
checkout := '1999-06-22 07:48:53.275';
...
END;

 When you declare a TIMESTAMP, you
provide a precision (from 0 to 9) for the
seconds component of the value.
 Use TIMESTAMP WITH TIME ZONE to
handle time zone displacement.

Copyright 2000-2005 Steven Feuerstein - Page 105
Working with Time Zones
DECLARE
logoff TIMESTAMP(3) WITH TIME ZONE;
logon TIMESTAMP(3) WITH LOCAL TIME ZONE;
BEGIN
logoff := '1999-10-31 09:42:37.114 +02:00';
...
END;

 Automatically work with the local time zone or specify a
specific displacement.
– Useful when gathering data that crosses time zones.
 The TIMEZONE_REGION and TIMEZONE_ABBR
columns of the V$TIMEZONE_NAMES data dictionary
view provide the names of available time zones.

tzset_show.sql
tzmisc.sql
tzglobal_events_local.sql
Copyright 2000-2005 Steven Feuerstein - Page 106
Interval Computations

 In the example below, declare a variable of type
INTERVAL YEAR TO MONTH, then assign a value
of 101 years and 3 months to it in three different
ways.
– These are not points in time, but amounts of elapsed time.

DECLARE
lifetime INTERVAL YEAR(3) TO MONTH;
BEGIN
lifetime := INTERVAL '101-3' YEAR TO MONTH; -- interval literal
lifetime := '101-3'; -- implicit conversion from character type
lifetime := INTERVAL '101' YEAR; -- Can specify just the years
lifetime := INTERVAL '3' MONTH; -- Can specify just the months
...
END;

Copyright 2000-2005 Steven Feuerstein - Page 107
Function with Interval

MEMBER FUNCTION age RETURN INTERVAL YEAR TO MONTH
IS
retval INTERVAL YEAR TO MONTH;
BEGIN
retval := (SYSDATE - SELF.dob) YEAR TO MONTH;
RETURN retval;
END;

 Notice the explicit conversion of the age
calculation formula to an INTERVAL.
person.ot

Copyright 2000-2005 Steven Feuerstein - Page 108
Lots of New Functions

 New conversion and "right now" capabilities:

EXTRACT
SESSIONTIMEZONE
NUMTODSINTERVAL
CURRENT_DATE
NUMTOYMINTERVAL
CURRENT_TIMESTAMP
TO_DSINTERVAL
DBTIMEZONE
TO_YMINTERVAL
LOCALTIMESTAMP
TO_TIMESTAMP
SYSTIMESTAMP
TO_TIMESTAMP_TZ
TZ_OFFSET
FROM_TZ

extract.sql

Copyright 2000-2005 Steven Feuerstein - Page 109
Working with XML in PL/SQL

 Brief introduction to XML
 The XMLtype datatype

Copyright 2000-2005 Steven Feuerstein - Page 110
What is XML?

 Stands for Extensible Markup Language and
defines a universal standard for electronic data
exchange...."EDI for the 21st century"
 Offers a rigorous set of rules to give structure to
data and make it easy to send and receive
information.
 Nothing but "text with tags"... a vendor-neutral,
platform-neutral, standards-based information
pathway.

Copyright 2000-2005 Steven Feuerstein - Page 111
Simple XML Example
<envReport>
<reportID>70689</reportID>
<source>ThickPlating, Inc.</source>
<Violations>
<Incident ReportedOn="01/15/2001"
ReportedBy="STEVEN.HARRISON">
Waste dumped in river</Incident>
<Incident ReportedOn="02/07/2001"
ReportedBy="SANDRA.HAJIJ">
Chimney filters disabled</Incident>
</Violations>
<NextSteps>
<Media>
Press conference to call for fines and penalties.
</Media>
<Legal>File suit in state and federal counts.</Legal>
</NextSteps>
</envReport>

Copyright 2000-2005 Steven Feuerstein - Page 112
Why Use XML?

 XML can serve as application integration "glue".
– XML over the HTTP protocol may provide the technology to
relatively seamlessly and inexpensively connect
heterogeneous databases, applications and networks.
 XML makes it easier to publish and reuse information.
– Complete separation of data from presentation layer.
– Standard utilities to transform data to specific output styles.
 XML is extensible.
– "Roll your own" markup language.

Copyright 2000-2005 Steven Feuerstein - Page 113
Extensions to XML

 XML is tabula rosa.
– You get to define the tags (you can think of HTML as a
subset of XML with pre-defined formatting tags).
 Different industries are now defining standardized
markup languages that know about those
industries' requirements. Some examples:
– ebXML electronic business infrastucture
– WML wireless markup language
– docBook documentation standards
www.xml.org

Copyright 2000-2005 Steven Feuerstein - Page 114
Send/Receive XML over Internet

 You can easily send XML documents over the Web
using:
– FTP - File Transfer Protocol (transfer files)
– SMTP - Simple Mail Transfer Protocol (transfer email)
– HTTP - HyperText Transfer Protocol (transfer documents)
 XML will likely figure prominently in the explosion of
wireless Internet devices.
– "Small footprint" of information-rich data.

Copyright 2000-2005 Steven Feuerstein - Page 115
Web Oracle 8i

XML
XML
Oracle
SAP Apps
Apps Mess
age
Hub

XSLT
Stylesheet
XML
SQL
Server

XML
Web

XML and HTTP can connect heterogeneous applications
Copyright 2000-2005 Steven Feuerstein - Page 116
Oracle Support for XML

 Oracle has moved very aggressively to
support XML from both Java and PL/SQL.
– Started in Oracle8i and accelerated tremendously
in Oracle9i: the XDB
 JDeveloper allows you create, syntax-check
and debug XML, XSLT and XSQL.
 interMedia lets you index and search an XML
document.
 Many utilities available through PL/SQL (next
page).
Copyright 2000-2005 Steven Feuerstein - Page 117
Some Oracle XML Components
Download the Oracle XDK from the Oracle Technology
Network; the 9i XDK works for 8i as well.

XML Parser Use it to parse, construct and validate XML
documents.
XPath Engine A utility that searches in-memory XML documents
using the declarative syntax of XPath, another
element of the XML standard
XSLT Supports XSLT in Oracle, allowing you to
Processor transform XML documents into different formats

XML SQL Utility to facilitate the production of XML
Utility documents from SQL and to easily insert XML-
based data into Oracle tables.
XSQL Pages Technology allowing you to assemble XML data
declaratively and then publish that data with XSLT.

Copyright 2000-2005 Steven Feuerstein - Page 118
Some XML Concepts and Capabilities

 The XML Document
 Document Type Definitions and
Schemas
– Similar to DDL for tables, DTDs and schemas
define valid syntax for an XML document
 The XML Infoset
– Tree representation of XML document
 XPath
– Search contents of XML documents

Copyright 2000-2005 Steven Feuerstein - Page 119
From Document to Infoset

Text Document
/ “Information Set”
<?xml version = “1.0”?>
<transaction><account>89-344</ac <transaction>
count><buy shares = “100”><ticker>
WEBM</ticker></buy><sell shares= <account>
“30”><ticker>INTC</ticker></sell
></transaction> 89-344

<buy> Shares = “100”

<ticker>

XML WEBM
Parse
r <sell> Shares = “30”

<ticker>

INTC

Copyright 2000-2005 Steven Feuerstein - Page 120
XPath: The XML Search Syntax

 W3C offers a declarative language called
XPath to query the contents of an XML
document.
– Operators on an information set
– Leverages our familiarity with the hierarchical
structure and path notation of directories and
URLs.
 The Oracle xslProcessor package
implements the XPath functionality.
Copyright 2000-2005 Steven Feuerstein - Page 121
Some XPath Examples
 What are the names of the newspaper in the
document?
/Newspaper/Name

 Does this company's CEO have any bonus
payments over $100,000 while hiring of new
employees was frozen?
//CEO/Bonus[. > 100000 and @HiringFreeze="ON"]

 When did Sheri S. Tepper publish "Grass" ?
/Publication/Novel[Title="Grass"]/@PublicationDate

Copyright 2000-2005 Steven Feuerstein - Page 122
Oracle9i The New XML Datatype

 A system-defined object type that has
predefined member functions available to
extract XML nodes and fragments.
 Brings the XML and SQL worlds together
– SQL operations on XML content
– XML operations on SQL content
– Apply standard XML functionality, such as XPath,
directly against data without need to convert.

Copyright 2000-2005 Steven Feuerstein - Page 123
Oracle9i Set of XMLtype Methods

 createXML: creates an XMLtype instance from a string
or CLOB.
 existsNode: returns 1 if the given XPath expression
returns any result nodes.
 extract: applies an XPath expression over the XML data
to return a XMLType instance containing the resultant
fragment.
 isFragment: returns 1 if the XMLtype contains a
fragment.
 getCLOBval, getStringval, getNumberval: returns an
XML document or fragment as CLOB, string or number.
Copyright 2000-2005 Steven Feuerstein - Page 124
Oracle9i Transfer XML Docs to Tables

 Create a table with an XMLtype column and
insert values with the CreateXML procedure.
CREATE TABLE xml_tab (xmlval SYS.XMLTYPE);

INSERT INTO xml_tab VALUES (
Oracle9i SYS.XMLTYPE.CREATEXML('<?xml version="1.0"?>
Release 1 Syntax <EMP>
<EMPNO>221</EMPNO>
<ENAME>John</ENAME>
</EMP>'));

INSERT INTO xml_tab VALUES (
Oracle9i XMLTYPE ('<?xml version="1.0"?>
Release 2 Syntax <PO>
<PONO>331</PONO>
<PONAME>PO_1</PONAME>
xmltype.sql
</PO>'));
Copyright 2000-2005 Steven Feuerstein - Page 125
Oracle9i Retrieve XML Data

SQL> select x.xmlval.getstringval() from xml_tab x;

X.XMLVAL.GETSTRINGVAL()
---------------------------------------------------------
<?xml version="1.0"?>
<EMP>
<EMPNO>221</EMPNO>
<ENAME>John</ENAME>
</EMP>

<?xml version="1.0"?>
<PO>
<PONO>331</PONO>
<PONAME>PO_1</PONAME>
</PO>

 Note: you must use an alias on the table name.
Copyright 2000-2005 Steven Feuerstein - Page 126
Oracle9i Other Examples of Integration

SELECT p.podocument.getclobval () "Document"
FROM purchaseorder p
WHERE XMLTYPE.EXTRACT (
p.podocument,
'/PurchaseOrder/User/text()').getstringval()
= 'SMITH';

 And function-based indexes based on
XPath!
CREATE UNIQUE INDEX i_purchase_order_reference
ON purchaseorder p
(
SUBSTR(SYS.XMLTYPE.GETSTRINGVAL(
SYS.XMLTYPE.EXTRACT(
p.PODOCUMENT,
'/PurchaseOrder/Reference/text()')),1,26)
)

Copyright 2000-2005 Steven Feuerstein - Page 127
Updating XML Documents in Tables

 Use updateXML to change the contents of an
XML document in a table.
– The following statement updates all rows
that have an employee element with the new
values.
UPDATE emp_tab e
SET e.emp_col = UPDATEXML (e.emp_col,
'/EMPLOYEES/EMP[EMPNAME="Joe"]/SALARY/text()',100000,
'//EMP[EMPNAME="Jack"]/EMPNAME/text()','Jackson',
'//EMP[EMPNO=217]',
XMLTYPE.CREATEXML(
'<EMP><EMPNO>217</EMPNO>
<EMPNAME>Jane</EMPNAME></EMP>'))
WHERE EXISTSNODE(e.emp_col, '//EMP') = 1;

Copyright 2000-2005 Steven Feuerstein - Page 128
Oracle's Just Getting Started

 Oracle is now referring to its XML-aware product as
XDB: the XML DataBase.
 Oracle9i Release 2 offers repository features to
manage XML data and documents, including:
– Access control lists for security
– Foldering, allowing for the creation of hierarchies of
directories and utilities to search and manage them.
– WebDAV** and FTP access

**Web-based Distributed Authoring and Versioning", HTTP extensions for
collaborative editing and management of files on remote web servers.

Copyright 2000-2005 Steven Feuerstein - Page 129
Generic Datatypes
 Generic datatypes allow you to create "anonymous"
TYPES, useful for highly generic table functions.
– SYS.ANYTYPE: description of any SQL type
– SYS.ANYDATA: instance of a given type, with data, plus a
description of the type. Persists in DB.
– SYS.ANYDATASET: description of a given type plus a set of
data instances of that type. Persists in DB.
 The DBMS_TYPES package provides a set of
constants to use with these generic, opaque
datatypes.
 Very minimal documentation but lots and lots of
potential.
rdbms/admin/dbmsany.sql
Copyright 2000-2005 Steven Feuerstein - Page 130 anynums.*
Data Types - Summary

 A broader, deeper choice for data types and
data structures offers great potential for
improving the quality and reducing the
quantity of your code base.
 Potentially dramatic impact on the quality of
your code.
 Get comfortable with the structures and put
them to work for you.

Copyright 2000-2005 Steven Feuerstein - Page 131
SQL-Related Enhancements in PL/SQL

 Native Dynamic SQL
 Bulk processing of DML and queries
 Record-based DML
 Autonomous transactions
 Table functions and CURSOR expressions

Copyright 2000-2005 Steven Feuerstein - Page 132
The Beauty and Elegance
of
Native Dynamic SQL

Copyright 2000-2005 Steven Feuerstein - Page 133
What is Dynamic SQL?

 Dynamic SQL actually refers, in the world of
PL/SQL, to two things:
– SQL statements, such as a DELETE or CREATE
TABLE, that are constructed and executed at run-
time.
– Anonymous PL/SQL blocks that are constructed,
compiled and executed at run-time.

'DROP ' || 'BEGIN ' ||
l_type || ' ' || l_name l_proc_name || ' (' ||
l_parameters || '); END;'

Copyright 2000-2005 Steven Feuerstein - Page 134
The Possibilities of Dynamic SQL

 Build ad-hoc query and update applications.
– When the user gets to decide what to do and see...a
common requirements for Internet applications.
 Execute DDL statements from within PL/SQL.
– They are not otherwise available in a PL/SQL block.
 Construct very generic and highly useful utilities
that work on "any" table or data structure.
 Optimize SQL statements at run-time through soft-
coding of hints.

Copyright 2000-2005 Steven Feuerstein - Page 135
Two Methods Available

 DBMS_SQL
– A large and complex built-in package that made
dynamic SQL possible in Oracle7 and Oracle8.
 Native Dynamic SQL
– A new (with Oracle8i), native implementation of
dynamic SQL that does almost all of what
DBMS_SQL can do, but much more easily and
usually more efficiently.

Let's focus on Native Dynamic SQL or NDS.

Copyright 2000-2005 Steven Feuerstein - Page 136
Native Dynamic SQL

 The new "native dynamic SQL" or NDS of
Oracle8i is implemented with just two
statements (which are part of the language and
not available via a built-in package):
– EXECUTE IMMEDIATE <sql string>, used for DDL,
DML and single row fetches.
– OPEN FOR <sql string>, used for multi-row queries.

And in Oracle9i Release 2, you can
even use EXECUTE IMMEDIATE
for multi-row queries!

Copyright 2000-2005 Steven Feuerstein - Page 137
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE sql-string

[INTO {define_variable[, define_variables]... | record }]

[USING {IN | OUT | IN OUT] bind argument
[, {IN | OUT | IN OUT] bind argument]...];

 Use this statement to execute any dynamic SQL
statement (including a PL/SQL block) except for multi-row
queries.
 The INTO clause allows you to pass values from the
select list of a single row query into local variables,
including objects, collections and records.
 The USING clause allows you to specify bind arguments
or variables to be passed into the SQL string before
execution.
Copyright 2000-2005 Steven Feuerstein - Page 138
DDL within PL/SQL

 Very easy, very dangerous with NDS.
– Here's a procedure that "drops whatever".
CREATE OR REPLACE PROCEDURE drop_whatever (nm IN VARCHAR2)
AUTHID CURRENT_USER
IS
CURSOR type_cur IS
SELECT object_type FROM USER_OBJECTS
WHERE object_name LIKE UPPER (nm);
type_rec type_cur%ROWTYPE; dropwhatever.sp
BEGIN creind81.sp
OPEN type_cur; FETCH type_cur INTO type_rec; health$.pkg
settrig.sp
IF type_cur%FOUND THEN
EXECUTE IMMEDIATE
'DROP ' || type_rec.object_type || ' ' || nm;
END IF;
END;
Copyright 2000-2005 Steven Feuerstein - Page 139
COUNT(*) For Any Table

 Here's a handy and simple utility based on
CREATE OR REPLACE FUNCTION tabCount (

NDS:
tab IN VARCHAR2, whr IN VARCHAR2 := NULL, sch IN VARCHAR2 := NULL)
RETURN INTEGER
IS Specify schema, table and
retval INTEGER;
BEGIN
WHERE clause...
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || NVL (sch, USER) ||
'.' || tab || ' WHERE ' || NVL (whr, '1=1') INTO retval;
RETURN retval;
END;

IF tabCount ('citizens', 'insured = ''NO''') > 40,000,000
THEN
DBMS_OUTPUT.PUT_LINE (
'Not the best health care system in the world....'); tabcount81.sf
END IF; tabcount.sf

Copyright 2000-2005 Steven Feuerstein - Page 140
DML with NDS

CREATE OR REPLACE PROCEDURE salary_raise (
raise_percent NUMBER, job VARCHAR2)
IS
TYPE loc_array_type IS TABLE OF offices.location@TYPE
INDEX BY BINARY_INTEGER;
Different
dml_str VARCHAR2 (200); table for
loc_array loc_array_type; each location
BEGIN
SELECT location BULK COLLECT INTO loc_array
FROM offices;

FOR i IN loc_array.FIRST .. loc_array.LAST LOOP
dml_str := 'UPDATE emp_' || loc_array (i)
|| ' SET sal = sal * (1+(:raise_percent/100))'
|| ' WHERE job = :job_title';
EXECUTE IMMEDIATE dml_str USING raise_percent, job;
END LOOP;
END;

Copyright 2000-2005 Steven Feuerstein - Page 141
Works w/User-defined types

 In the following example, the USING clause allows
me to pass an object and nested table to an
INSERT statement with a variable table name.
– Completely transparent support.

PROCEDURE add_profit_source (
hosp_name IN VARCHAR2,
pers IN Person,
cond IN preexisting_conditions)
IS
BEGIN health$.pkg
EXECUTE IMMEDIATE
'INSERT INTO ' || tabname (hosp_name) ||
' VALUES (:revenue_generator, :revenue_inhibitors)'
USING pers, cond;
END;
Copyright 2000-2005 Steven Feuerstein - Page 142
Multiple Row Queries and NDS
 Familiar syntax, tiny learning curve: OPEN FOR
– Here is a simple utility that displays the values of any date,
number or string column in any table.
CREATE OR REPLACE PROCEDURE showcol (
tab IN VARCHAR2, col IN VARCHAR2, whr IN VARCHAR2 := NULL)
IS
cv SYS_REFCURSOR; -- Available in Oracle9i
val VARCHAR2(32767);
BEGIN
OPEN cv FOR 'SELECT ' || col || ' FROM ' || tab ||
' WHERE ' || NVL (whr, '1 = 1');
LOOP
FETCH cv INTO val;
EXIT WHEN cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (val);
END LOOP; showcol81.sp
CLOSE cv; ndsutil.pkg
END;
Copyright 2000-2005 Steven Feuerstein - Page 143
Fetch Into Records!

 In DBMS_SQL, you had to write many tedious
lines of code to fetch into individual variables.
CREATE OR REPLACE PROCEDURE showemps (where_in IN VARCHAR2 := NULL)
IS
cv SYS_REFCURSOR;
rec employee%ROWTYPE; New (Oracle9i) pre-
BEGIN
OPEN cv FOR
defined weak REF
'SELECT * FROM employee CURSOR type
WHERE ' || NVL (where_in, '1=1');
LOOP
FETCH cv INTO rec;
EXIT WHEN cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (
TO_CHAR (rec.employee_id) || '=' || rec.last_name);
END LOOP;
CLOSE cv;
END;
Copyright 2000-2005 Steven Feuerstein - Page 144
Quiz!

PROCEDURE process_lineitem (
 What's wrong with line_in IN INTEGER)
IS
this code? BEGIN
IF line_in = 1
 How would you fix THEN
process_line1;
it? END IF;

IF line_in = 2
THEN
process_line2;
END IF;
...
IF line_in = 22045
THEN
process_line22045;
END IF;
END;

Copyright 2000-2005 Steven Feuerstein - Page 145 dynplsql.txt
From 22,000 lines of code to 1!
PROCEDURE process_lineitem (
line_in IN INTEGER)
IS
BEGIN PROCEDURE process_lineitem (
IF line_in = 1 line_in IN INTEGER)
THEN IS
process_line1; BEGIN
END IF; EXECUTE IMMEDIATE
'BEGIN process_line'||
IF line_in = 2 line_in ||'; END;';
THEN END;
process_line2;
END IF;
...
IF line_in = 22045  Identify the pattern and
THEN
process_line22045; resolve it either with
END IF;
END; reusable modules or
dynamic abstractions.
Copyright 2000-2005 Steven Feuerstein - Page 146 dynplsql.txt
Dynamic PL/SQL with NDS

 Dynamically construct, compile and run an
anonymous block with EXEC. IMMEDIATE.
 DBMS_JOB uses dynamic PL/SQL to run
stored procedures at scheduled times.
DECLARE
v_jobno INTEGER;
BEGIN
DBMS_JOB.submit (
job => v_jobno,
what => 'DBMS_DDL.ANALYZE_OBJECT ' ||
'(''TABLE'',''LOAD1'',''TENK''' ||
',''ESTIMATE'',null,estimate_percent=>50);',
next_date => TRUNC (SYSDATE + 1),
interval => 'TRUNC(SYSDATE+1)'
);
END;
Copyright 2000-2005 Steven Feuerstein - Page 147
Rules for Dynamic PL/SQL

 You must construct and execute a valid
anonymous block.
– Begins with BEGIN or DECLARE.
– Ends with END;. The trailing semi-colon is
required; otherwise it is parsed as dynamic
SQL.
 You can only reference globally-accessible data
structures (declared in a package specification).
 Exceptions can (and should) be trapped in the
block from which the dynamic PL/SQL was
executed. dynplsql8i.sp
dynplsql_nolocal.sql
Copyright 2000-2005 Steven Feuerstein - Page 148
Dynamic PL/SQL Possibilities

 There are so many possibilities....some things I
have done:
– Dramatically reduce code volume, improve
performance.
– Generic string parsing engine: parse any string into
your own collection.
– Generic calculator engine.
– Implement support for "indirect referencing": read
and change values of variables whose names are
only determined at run-time.
str2list.pkg
filepath*.*
dynvar81.pkg
dyncalc.pkg
Copyright 2000-2005 Steven Feuerstein - Page 149
The utPLSQL Architecture

 utPLSQL.test constructs the names of and executes
the setup, unit test and teardown procedures based
on the package name you provide.
Your Test Package
1 Assertion API
ut_Setup
2
utPLSQL.test ut_TestMe Assert EQ
Test Engine
ut_Teardown Assert NULL

Assert EqTable

Report Results Test for Nulls
4 Invalid ID
5 Valid ID 3
Start date too late
Results Table End date too early
Name unique
http://utplsql.sourceforge.net/
Copyright 2000-2005 Steven Feuerstein - Page 150 http://utplsql.oracledeveloper.nl/
Method 4 Dynamic SQL: NDS and
DBMS_SQL

 Method 4 dynamic SQL is the most generalized
and most complex - by far!
– You don't know at compile time either the number of
columns or the number of bind variables.
– With DBMS_SQL, you must put calls to
DBMS_SQL.DEFINE_COLUMN and/or
DBMS_SQL.BIND_VARIABLE into loops.
 With NDS, you must shift from dynamic SQL to
dynamic PL/SQL.
– How else can you have a variable INTO or
USING clause?
Copyright 2000-2005 Steven Feuerstein - Page 151
Dynamic "SELECT * FROM <table>"
in PL/SQL

 You provide the table and WHERE clause. I
display all the data.
– I don't know in advance which or how
many rows to query.
 I can obtain the column information from
ALL_TAB_COLUMNS...and from there the
fun begins!
 Let's compare the DBMS_SQL and NDS
implementations. intab.sp
intab9i.sp
intab9i.tst
Copyright 2000-2005 Steven Feuerstein - Page 152
Pseudo-code flow for
DBMS_SQL implementation
BEGIN
Build the FOR each-column-in-table LOOP
SELECT list add-column-to-select-list;
END LOOP;
Parse the
DBMS_SQL.PARSE (cur, select_string, DBMS_SQL.NATIVE);
variable SQL
FOR each-column-in-table LOOP
Define each
DBMS_SQL.DEFINE_COLUMN (cur, nth_col, datatype);
column
END LOOP;

Execute the fdbk := DBMS_SQL.EXECUTE (cur);
query
LOOP
fetch-a-row;
Extract each FOR each-column-in-table LOOP
value DBMS_SQL.COLUMN_VALUE (cur, nth_col, val);
END LOOP;
END LOOP;
Lots of code, but relatively
END;
straightforward
Copyright 2000-2005 Steven Feuerstein - Page 153
The method 4 challenge
for NDS

 It's not too difficult to build the SELECT list (I can even
use BULK COLLECT against ALL_TAB_COLUMNS to
speed up the process).
 But I still have to fetch that data back into local
variables. How many? Who knows! And the INTO
clause is static.
How about this?
EXECUTE IMMEDIATE
This EXECUTE IMMEDIATE
'SELECT ' || col_list || 'SELECT ' || col_list ||
won't ' FROM ' || table_in || ' FROM ' || table_in ||
' WHERE ' || where_clause
work: ' WHERE ' || where_clause
INTO col1, col2 ... ? INTO || into_list;

It looks like, it must be...
Copyright 2000-2005 Steven Feuerstein - Page 154 dynamic PL/SQL!
Shifting paradigms to dynamic PL/SQL

 So now I am thinking about building the entire block
to fetch from and display the data.
– In which case, I can put static SQL inside my
dynamic PL/SQL.
Construct a static
BEGIN query inside a CFL
l_block :=
'BEGIN
FOR rec IN (' || query_string || ')
It's all one big LOOP
PL/SQL block pl (' || concatenated_values || ');
END LOOP;
END;';
EXECUTE IMMEDIATE l_block; Concatenate all retrieved
END; values into single string
expression.

No need for INTO
Copyright 2000-2005 Steven Feuerstein - Page 155
or USING clauses
Oracle8i
Some Fine Print for NDS
 You cannot pass schema elements (table names,
column names, etc.) through the USING clause.
 The NULL literal may not be passed directly in USING.
Instead, pass a variable with a NULL value.
 The USING clause for a query can only have IN bind
arguments.
 You can have duplicate placeholders (for bind
arguments).
– If dynamic SQL, provide value for each placeholder (by
position).
– If dynamic PL/SQL, provide value for each distinct
placeholder (by name).
dupbind.sql
Copyright 2000-2005 Steven Feuerstein - Page 156
Dangers of Dynamic SQL
 Common tradeoff: flexibility vs. performance
– Loss of dependency information in the data
dictionary
 Runtime compilation is always slower than
pre-compilation
– Extra parsing/optimization (CPU) and memory
allocation
– May only be problematic when volume is large, so
don’t be dogmatic about it!
 Dynamic DDL can really be a killer
– Library cache object invalidation/recompilation
Copyright 2000-2005 Steven Feuerstein - Page 157
Recommendations for NDS

 Bind (vs. concatenate) whenever possible/appropriate.
– Increased chance of reusing parsed SQL, and easier code to
write.
– But you can reduce the effectiveness of the cost-based
optimizer by giving it less information to work with.
 Handle errors gracefully and comprehensively.
– Trap exceptions and display the problematic SQL to make it
easier to track down and resolve the problem.
 Use AUTHID CURRENT_USER for all stored programs
that contain dynamic SQL.
– Otherwise that SQL will be executed under the authority
of the owner of the code, not the invoker of the code.
whichsch*.sql
Copyright 2000-2005 Steven Feuerstein - Page 158
Choose Binding over Concatenation
 Simpler code to build and maintain
– Concatenation results in much more complicated and error-
prone code unless you are doing a very simple operation
 Improved application performance?
– Concatenates leads to physically different statements,
requiring re-parsing and additional SGA utilization
– But you lose information useful to the cost-based optimizer.
 You can only bind variable values.
– Schema elements, like table names, or portions of the SQL
statement, must be concatenated.
usebinding.sp
useconcat.sp
toomuchbind.sp
Copyright 2000-2005 Steven Feuerstein - Page 159
Handle Dynamic SQL Errors

 Handle errors gracefully and comprehensively.
– Do not assume that the SQL statement will be
constructed properly.
 Most important during parse phase with
DBMS_SQL.
– It can very difficult to figure out what went wrong,
so trap exceptions and display the problematic SQL
to make it easier to track down and resolve the
problem.
dynerrhdlg.sp
openprse.pkg

Copyright 2000-2005 Steven Feuerstein - Page 160
NDS or DBMS_SQL: Which is best?

 Reasons to go with NDS:  Why You'd Use DBMS_SQL:
– Ease of use – Method 4 Dynamic SQL
– Performance (usually – DESCRIBE columns of
faster) cursor
– Works with all SQL – SQL statements larger
datatypes (including than 32K
user-defined object – Better reuse of parsed
and collection types) SQL statements --
– Fetch into records persistent cursor
and collections of handles!
records (9iR2) – Available from client-side
PL/SQL
Bottom line: NDS should be your first choice.
Copyright 2000-2005 Steven Feuerstein - Page 161
Oracle8i SQL on Steroids: Bulk Processing
Oracle9i

 Oracle8i and Oracle9i offer groundbreaking new
syntax to improve the performance of both DML and
queries.
 In Oracle8, updating from a collection (or, in
general, performing multi-row DML) meant writing
code like this:
CREATE TYPE dlist_t AS TABLE OF INTEGER;
/
PROCEDURE remove_emps_by_dept (deptlist dlist_t)
IS “Conventio
BEGIN nal binds”
FOR aDept IN deptlist.FIRST..deptlist.LAST
LOOP (and lots of
DELETE emp WHERE deptno = deptlist(aDept); them!)
END LOOP;
END; Steven Feuerstein - Page 162
Copyright 2000-2005
Conventional Bind
Oracle server

PL/SQL Runtime Engine SQL Engine
PL/SQL block
Procedural
statement
FOR aDept IN deptlist.FIRST.. executor
deptlist.LAST
LOOP
SQL
DELETE emp statement
WHERE deptno = deptlist(aDept);
END LOOP; executor

Performance penalty
for many “context
switches”

Copyright 2000-2005 Steven Feuerstein - Page 163
Enter the “Bulk Bind”
Oracle server

PL/SQL Runtime Engine SQL Engine
PL/SQL block
Procedural
statement
FORALL aDept IN deptlist.FIRST.. executor
deptlist.LAST
DELETE emp
SQL
WHERE deptno = deptlist(aDept); statement
executor

Much less overhead for
context switching

Copyright 2000-2005 Steven Feuerstein - Page 164
Use the FORALL Bulk Bind Statement
 Instead of the individual DML operations, you can
do this:
PROCEDURE remove_emps_by_dept (deptlist dlist_t)
IS
BEGIN
FORALL aDept IN deptlist.FIRST..deptlist.LAST
DELETE FROM emp WHERE deptno = deptlist(aDept);
END;

 Some restrictions:
– Only the single DML statement is allowed. If you want
to INSERT and then UPDATE, two different FORALL
statements
– Cannot put an exception handler on the DML statement
-- until Oracle9i Release 2.
Copyright 2000-2005 Steven Feuerstein - Page 165
Use BULK COLLECT INTO for Queries

Oracle8i requires CREATE OR REPLACE FUNCTION process_emps
fetching into individual (deptno_in IN dept.depno%TYPE)
IS
collections of scalars. TYPE numTab IS TABLE OF NUMBER;
TYPE charTab IS TABLE OF VARCHAR2(12);
TYPE dateTab IS TABLE OF DATE;
enos numTab;
CREATE OR REPLACE FUNCTION process_emps
names charTab;
(deptno_in IN dept.depno%TYPE)
hdates dateTab;
IS
BEGIN
TYPE three_cols_rt IS RECORD (
SELECT empno, ename, hiredate
empno emp.empno%TYPE,
BULK COLLECT INTO enos, names, hdates
ename emp.ename%TYPE,
FROM emp
hiredate emp.hiredate%TYPE);
WHERE deptno = deptno_in;
TYPE three_cols_tt IS TABLE OF
FOR i IN enos.FIRST..enos.LAST
three_cols_rt INDEX BY PLS_INTEGER;
LOOP
three_cols_t three_cols_tt;
do_stuff (enos(i),
BEGIN
names(i), hiredates(i));
SELECT empno, ename, hiredate
END LOOP;
BULK COLLECT INTO three_cols_t
END;
FROM emp Oracle9i R2 supports
WHERE deptno = deptno_in;
...
fetching into a collection
END; of records.
Copyright 2000-2005 Steven Feuerstein - Page 166
Limit the number of rows returned by
BULK COLLECT
CREATE OR REPLACE PROCEDURE bulk_with_limit
(deptno_in IN dept.deptno%TYPE)
IS Use the LIMIT clause with the
CURSOR emps_in_dept_cur IS INTO to manage the amount
SELECT *
FROM emp
of memory used with the
WHERE deptno = deptno_in; BULK COLLECT operation.

TYPE emp_tt IS TABLE OF emp%ROWTYPE;
emps emp_tt;
BEGIN WARNING!
OPEN three_cols_cur;
LOOP
FETCH emps_in_dept_cur BULK COLLECT will not raise
BULK COLLECT INTO emps NO_DATA_FOUND if no rows
LIMIT 100; are found.
EXIT WHEN emps_in_dept_cur%NOTFOUND = 0;
process_emps (emps);
END LOOP; Either check cursor status or
END bulk_with_limit; contents of collection to
confirm that something was
retrieved.
Copyright 2000-2005 Steven Feuerstein - Page 167
Combining FORALL & BULK COLLECT

 Use the RETURNING clause to obtain information
about each DML statement executed with FORALL
– When executing multiple DML statements, you need to BULK
COLLECT the RETURNING results into one or more
collections
FUNCTION remove_emps_by_dept (deptlist dlist_t)
RETURN enolist_t
IS
enolist enolist_t;
BEGIN
FORALL aDept IN deptlist.FIRST..deptlist.LAST
DELETE FROM emp WHERE deptno IN deptlist(aDept)
RETURNING empno BULK COLLECT INTO enolist; bulkcoll.sql
RETURN enolist; bulktiming.sql
END;

Copyright 2000-2005 Steven Feuerstein - Page 168
Tips and Fine Points

 Use bulk binds in these circumstances:
– Recurring SQL statement in PL/SQL loop
– Use of a collection as the bind variable, or code that could
be transformed to use a collection containing the bind
variable information
 Bulk bind rules:
– Can be used with any kind of collection; Collection
subscripts cannot be expressions; The collections must be
densely filled (pre-10g); If error occurs, prior successful
DML statements are NOT ROLLED BACK
 Bulk collects:
– Can be used with implicit and explicit cursors
Collection is filled starting at row 1
Copyright 2000-2005 Steven Feuerstein - Page 169
Oracle9i Oracle9i Enhancements

 You can now use dynamic SQL strings in
the bulk bind and collect statements.
– FORALL for bulk DML
– BULK COLLECT for bulk queries.

 This gives you virtually unlimited flexibility
without a tradeoff in performance.

Copyright 2000-2005 Steven Feuerstein - Page 170
Oracle9i
Dynamic FORALL Example
 This example shows the use of bulk binding and
collecting, plus application of the RETURNING clause.
CREATE TYPE NumList IS TABLE OF NUMBER;
CREATE TYPE NameList IS TABLE OF VARCHAR2(15);

PROCEDURE update_emps (
col_in IN VARCHAR2, empnos_in IN numList) IS
enames NameList;
BEGIN
FORALL indx IN empnos_in.FIRST .. empnos_in.LAST
EXECUTE IMMEDIATE
'UPDATE emp SET ' || col_in || ' = ' || col_in
|| ' * 1.1 WHERE empno = :1
RETURNING ename INTO :2'
USING empnos_in (indx ) Notice that empnos_in
RETURNING BULK COLLECT INTO enames; is indexed, but enames
... is not.
END;
Copyright 2000-2005 Steven Feuerstein - Page 171
Oracle9i
Dynamic BULK COLLECT
 Now you can even avoid the OPEN FOR and just
grab your rows in a single pass!
CREATE OR REPLACE PROCEDURE fetch_by_loc (loc_in IN VARCHAR2)
IS
TYPE numlist_t IS TABLE OF NUMBER;
TYPE namelist_t IS TABLE OF VARCHAR2 (15);
emp_cv sys_refcursor; Both
empnos numlist_t; approaches
enames namelist_t; work
sals numlist_t;
BEGIN
OPEN emp_cv FOR 'SELECT empno, ename FROM emp_' || loc_in;
FETCH emp_cv BULK COLLECT INTO empnos, enames;
CLOSE emp_cv; With
Oracle9iR2
EXECUTE IMMEDIATE 'SELECT sal FROM emp_' || loc_in you can also
BULK COLLECT INTO sals; fetch into
END; collections of
Copyright 2000-2005 Steven Feuerstein - Page 172 records.
Better Exception Handling
Oracle9i
for Bulk Operations
 Allows you to continue past errors and obtain
error information for each individual
operation (for dynamic and static SQL).
CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t)
IS
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGIN
FORALL indx IN books_in.FIRST..books_in.LAST Allows processing of all
SAVE EXCEPTIONS rows, even after an error
INSERT INTO book values (books_in(indx)); occurs.
EXCEPTION
WHEN BULK_ERRORS THEN
FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT New cursor
LOOP attribute, a pseudo-
log_error (SQL%BULK_EXCEPTIONS(indx)); collection
END LOOP;
END;
Copyright 2000-2005 Steven Feuerstein - Page 173 bulkexc.sql
Cursor FOR Loop ... or BULK COLLECT?

 Why would you ever use a cursor FOR loop
(or other LOOP) now that you can perform a
BULK COLLECT?
– If you want to do complex processing on each
row as it is queried – and possibly halt further
fetching.
– You are retrieving many rows and cannot afford
to use up the memory (large numbers of users).
 Otherwise, moving to BULK COLLECT is a
smart move!
cfl_vs_bulkcollect.sql
cfl_to_bulk.sql
Copyright 2000-2005 Steven Feuerstein - Page 174
Oracle10g More flexibility with FORALL

 In Oracle10g, the FORALL driving array no
longer needs to be processed sequentially.
 Use the INDICES OF clause to use only the
row numbers defined in another array.
 Use the VALUES OF clause to use only the
values defined in another array.

Copyright 2000-2005 Steven Feuerstein - Page 175
Oracle10g Using INDICES OF

 It only DECLARE
TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
processes INDEX BY PLS_INTEGER;
l_employees employee_aat;
the rows TYPE boolean_aat IS TABLE OF BOOLEAN
INDEX BY PLS_INTEGER;
with row l_employee_indices boolean_aat;
BEGIN
numbers l_employees (1) := 7839;
matching l_employees (100) := 7654;
l_employees (500) := 7950;
the --
l_employee_indices (1) := TRUE;
defined l_employee_indices (500) := TRUE;
--
rows of FORALL l_index IN INDICES OF l_employee_indices
UPDATE employee
the driving SET salary = 10000
array. END;
WHERE employee_id = l_employees (l_index);

Copyright 2000-2005 Steven Feuerstein - Page 176 10g_indices_of.sql
Oracle10g Using VALUES OF

DECLARE
 It only TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
INDEX BY PLS_INTEGER;
processes l_employees employee_aat;
the rows TYPE indices_aat IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
with row l_employee_indices
BEGIN
indices_aat;

numbers l_employees (-77) := 7820;
l_employees (13067) := 7799;
matching l_employees (99999999) := 7369;
--
the content l_employee_indices (100) := -77;
of a row in l_employee_indices (200) := 99999999;
--
the driving FORALL l_index IN VALUES OF l_employee_indices
UPDATE employee
array. SET salary = 10000
WHERE employee_id = l_employees (l_index);
END;

Copyright 2000-2005 Steven Feuerstein - Page 177 10g_values_of.sql
Oracle9i RECORD-BASED DML

 PL/SQL records (similar in structure to a row
in a table) offer powerful ways to manipulate
data
– Prior to Oracle9i R2, however, records could not
be used in DML statements
 That restriction has now been lifted
– You can INSERT specifying a record rather than
individual fields of the record
– You can UPDATE an entire row with a record

Copyright 2000-2005 Steven Feuerstein - Page 178
RECORD-BASED INSERTS

DECLARE
TYPE book_list_t IS TABLE OF books%ROWTYPE;
my_books book_list_t := book_list_t();
BEGIN
my_books.EXTEND (2);

my_books(1).isbn := '1-56592-335-9';
my_books(1).title := 'ORACLE PL/SQL PROGRAMMING';

my_books(2).isbn := '0-596-00121-5';
my_books(2).title := 'ORACLE PL/SQL BEST PRACTICES';

FORALL indx IN my_books.FIRST .. my_books.LAST
INSERT INTO books VALUES my_books(indx);
END;

 This example shows a record-based insert
inside the high-speed FORALL statement
Copyright 2000-2005 Steven Feuerstein - Page 179
RECORD-BASED UPDATES

DECLARE
my_book books%ROWTYPE;
BEGIN
my_book.isbn := '1-56592-335-9';
my_book.title := 'ORACLE PL/SQL PROGRAMMING';
my_book.summary := 'General user guide and reference';
my_book.author := 'FEUERSTEIN, STEVEN AND BILL PRIBYL';
my_book.page_count := 950; -- new page count for 3rd edition

UPDATE books
SET ROW = my_book
WHERE isbn = my_book.isbn;
END;

 You can only update the entire ROW, and not a
subset via, say, a programmer-defined record type

Copyright 2000-2005 Steven Feuerstein - Page 180
Autonomous Transactions

 Prior to Oracle8i, a COMMIT or ROLLBACK in any
program in your session committed or rolled back
all changes in your session.
– There was only one transaction allowed per connection.
 With Oracle8i, you can now define a PL/SQL block
to execute as an "autonomous transaction".
– Any changes made within that block can be saved or
reversed without affecting the outer or main transaction.

CREATE OR REPLACE PROCEDURE loginfo (
code IN PLS_INTEGER,
msg IN VARCHAR2)
AS
PRAGMA AUTONOMOUS_TRANSACTION;

Copyright 2000-2005 Steven Feuerstein - Page 181
When to Use Autonomous Transactions

 Reusable Application Components
– ATs are more or less required in the new distributed
application architecture of the Internet.
 Logging Mechanism
– Solves problems of error logs in database tables,
with log entries a part of your transaction.
 Call functions within SQL that change the
database.
 Issue commits and rollbacks inside DB triggers.

Copyright 2000-2005 Steven Feuerstein - Page 182
Autonomous vs. Nested Transactions

 An AT is started by another transaction, but is not a
nested transaction for the following reasons:
– It does not share transactional resources (such as locks) with
the main transaction.
– It does not depend on the main transaction. For example, if
the main transaction rolls back, nested transactions roll back,
but autonomous transactions do not.
– Its committed changes are visible to other transactions
immediately. (A nested transaction's committed changes are
not visible to other transactions until the main transaction
commits.)

Copyright 2000-2005 Steven Feuerstein - Page 183
Logging with ATs
CREATE OR REPLACE PACKAGE BODY log
IS
PROCEDURE putline (
code_in IN INTEGER, text_in IN VARCHAR2 Avoid inter-
) dependencies with
IS the main
PRAGMA AUTONOMOUS_TRANSACTION; transaction.
BEGIN
INSERT INTO logtab
VALUES (code_in, text_in,
SYSDATE, USER, SYSDATE, USER,
rec.machine, rec.program
); Save on
COMMIT; successful exit
EXCEPTION Don't forget to
WHEN OTHERS THEN ROLLBACK;
rollback on error!
END;
END;
logger.sp
retry.pkg
log81.pkg
retry.tst
log81*.tst
Copyright 2000-2005 Steven Feuerstein - Page 184
Tips and Gotchas with ATs

 An AT program that executes DML must COMMIT or
ROLLBACK before terminating, or an error is raised.
– If you only query, COMMIT/ROLLBACK is not required.
 The AT PRAGMA can be used only with individual
programs and top-level anonymous blocks.
– You cannot define an entire package as an AT.
– You cannot define a nested anonymous block as an AT.
 The AT PRAGMA goes in the body of packages.
– You cannot tell by looking at the package spec if you are
calling ATs or not -- and this info is not available in the
data dictionary.
Copyright 2000-2005 Steven Feuerstein - Page 185
Tips and Gotchas, cont.
 The Oracle initialization parameter
TRANSACTIONS specifies the maximum number of
concurrent transactions.
– Which might be exceeded if autonomous transactions
(running concurrently with main transaction) are not taken
into account.
 Any changes committed in an AT are visible in the
outer transaction.
– You can use the SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE to indicate that you do not want
the changes visible until the outer transaction commits.
– Place the SET TRANSACTION statement in the outer
transaction.
autonserial.sql
Copyright 2000-2005 Steven Feuerstein - Page 186 auton_in_sql.sql
autontrigger*.sql
Autonomous Transactions - Summary

 Easy to define
 Lots of immediate applications
 Minimal learning curve
 Low implementation risks

 You should immediately explore
opportunities to utilize this feature.

Copyright 2000-2005 Steven Feuerstein - Page 187
Table Functions and
CURSOR Expressions

 A CURSOR expression returns a nested
cursor.
– They were available in SQL earlier, but are new
to PL/SQL in Oracle9i.
– You can now pass cursors as arguments to
programs by relying on the REF CURSOR
syntax (cursor variables already point to cursors).
 Table functions are functions that can be
included in the FROM clause of a SELECT
list

Copyright 2000-2005 Steven Feuerstein - Page 188
Querying with
1 of 2
Cursor Expressions

CREATE OR REPLACE PROCEDURE emp_report (p_locid NUMBER)
IS
CURSOR c1 is Nested cursor
SELECT l.city, expressions instead
CURSOR(SELECT d.department_name, of multiple explicit
CURSOR( cursors.
SELECT e.last_name
FROM employees e
WHERE e.department_id = d.department_id) as ename
FROM departments d where l.location_id = d.location_id) dname
FROM locations l
WHERE l.location_id = p_locid;

loccur SYS_REFCURSOR;
deptcur SYS_REFCURSOR;

v_city locations.city%TYPE;
v_dname departments.department_name%TYPE;
v_ename employees.last_name%TYPE;
...
cursor_expr.sql
Copyright 2000-2005 Steven Feuerstein - Page 189
Fetching from Cursor Expressions
2 of 2

CREATE OR REPLACE PROCEDURE emp_report (p_locid NUMBER)
IS ... <see previous page>
BEGIN Retrieve an entire
OPEN c1; cursor; already
LOOP
open.
FETCH c1 INTO v_city, loccur;
EXIT WHEN c1%NOTFOUND;
LOOP
FETCH loccur
INTO v_dname, deptcur; -- No need to open
EXIT WHEN loccur%NOTFOUND;

LOOP
FETCH deptcur INTO v_ename; -- No need to open
EXIT WHEN deptcur%NOTFOUND;
DBMS_OUTPUT.put_line (
v_city || ' ' || v_dname || ' ' || v_ename
);
END LOOP;
END LOOP;
END LOOP;
CLOSE c1;
END;
Copyright 2000-2005 Steven Feuerstein - Page 190
Opening and Closing
Cursor Expressions

 A nested cursor is implicitly opened when the
containing row is fetched from the parent
cursor.
 The nested cursor is closed only when:
– The nested cursor is explicitly closed by the user
– The parent cursor is re-executed
– The parent cursor is closed
– The parent cursor is canceled
– An error arises during a fetch on one of its parent
cursors. The nested cursor is closed as part of
the clean-up.
Copyright 2000-2005 Steven Feuerstein - Page 191
Cursor Expressions as Parameters

 A CURSOR expression returns a cursor. A
cursor variable points to a cursor. So...

CREATE OR REPLACE FUNCTION most_popular (
cursor_in IN sys_refcursor) RETURN INTEGER...

SELECT *
FROM hairstyles
WHERE popularity =
most_popular (
CURSOR (SELECT *
FROM hairstyles
WHERE code < 2000));

cursor_expr2.sql
Copyright 2000-2005 Steven Feuerstein - Page 192
Tips and Gotchas: Cursor Expressions

 You cannot use a cursor expression with an implicit
cursor.
 Cursor expressions can appear only:
– In a SELECT statement that is not nested in any other
query expression, except when it is a subquery of the
cursor expression itself.
– As arguments to table functions, in the FROM clause of a
SELECT statement.
– In the outermost SELECT list of the query specification.
 Cursor expressions cannot appear in view
declarations.
Copyright 2000-2005 Steven Feuerstein - Page 193
Exploring Table Functions

SELECT c.name, Book.name, Book.author, Book.abstract
FROM Catalogs c,
TABLE (te_Book.result_set (c.cat)) Book;

 Table functions return a collection type instance
and can be queried like a table by calling the
function in the FROM clause of a query.
 If the function accepts as its IN argument a REF
CURSOR (new to Oracle9i), then it can also serve
as a "transformative" function.
– Pass results sets from one function to another without
the need for intermediate data structures.
Copyright 2000-2005 Steven Feuerstein - Page 194
Benefits of Table Functions

 Improved performance, particularly for data
warehouse applications.
– Full support for parallel processing.
• Enables multi-threaded, concurrent execution of
table functions.
• Eliminates intermediate staging between processes.
• Allows iterative return of result set; rows can be
returned as they are identified, before the function
execution ends.

 Increased language flexibility.
– Better encapsulation of complex logic.
– Allow emulation of nested tables as relational data.

Copyright 2000-2005 Steven Feuerstein - Page 195
Oracle8i Table Function Example

 Create an object TYPE and a nested table TYPE
of those objects.
 Then define a function that returns a nested
table of this type.
CREATE TYPE pet_t IS OBJECT (
NAME VARCHAR2 (60),
breed VARCHAR2 (100),
dob DATE);
/
CREATE TYPE pet_nt IS TABLE OF pet_t;
/
CREATE OR REPLACE FUNCTION pet_family (
dad_in IN pet_t, mom_in IN pet_t) Continued...
RETURN pet_nt ...

Copyright 2000-2005 Steven Feuerstein - Page 196
Table Function Example, cont.
 Populate the collection, and then use it in a query.
CREATE OR REPLACE FUNCTION pet_family (
dad_in IN pet_t, mom_in IN pet_t) RETURN pet_nt
IS
l_count PLS_INTEGER;
retval pet_nt := pet_nt ();
BEGIN
retval.EXTEND; retval (retval.LAST) := dad_in;
retval.EXTEND; retval (retval.LAST) := mom_in;

IF mom_in.breed = 'RABBIT' THEN l_count := 12;
ELSIF mom_in.breed = 'DOG' THEN l_count := 4;
ELSIF mom_in.breed = 'KANGAROO' THEN l_count := 1;
END IF;

FOR indx IN 1 .. l_count LOOP SELECT * FROM TABLE (CAST (
retval.EXTEND; pet_family (
retval (retval.LAST) :=
pet_t ('Hoppy', 'RABBIT', SYSDATE),
pet_t (
'BABY' || indx, pet_t ('Hippy', 'RABBIT', SYSDATE)
mom_in.breed, SYSDATE); ) AS pet_nt));
END LOOP;
RETURN retval;
Copyright 2000-2005 Steven Feuerstein - Page 197 tabfunc3.sql
END;
Oracle9i Table Function Enhancements

 You can now use the CURSOR expression
syntax to pass a result set as an argument to
a table function.
– This function can then be called in top-level
queries.
 Table functions can now be "pipelined",
allowing data to be returned iteratively.
– Such a function can be executed in parallel,
offering significant performance improvements in
data warehousing applications.

Copyright 2000-2005 Steven Feuerstein - Page 198
Passing Cursors as Arguments

Define a REF
CREATE OR REPLACE PACKAGE refcur_pkg IS CURSOR type
TYPE refcur_t IS REF CURSOR
RETURN StockTable%ROWTYPE;
END refcur_pkg;
Create a function that
CREATE OR REPLACE FUNCTION StockPivot (
accepts a cursor of
cur_in refcur_pkg.refcur_t)
that type.
RETURN TickerTypeSet...

INSERT INTO tickertable
SELECT *
FROM TABLE (StockPivot (
CURSOR (SELECT * FROM StockTable)));

tabfunc.sql

Call the function from within SQL,
passing to it another query.

Copyright 2000-2005 Steven Feuerstein - Page 199
Working w/Pipelined Functions

CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t)
RETURN TickerTypeSet PIPELINED

 Pipelined functions allow you to return data
iteratively.
– As data is produced within the function, it is passed
back to the calling process/query.
 Pipelined functions can be defined to support
parallel execution.
– Iterative data processing allows multiple processes to
work on that data simultaneously.

Copyright 2000-2005 Steven Feuerstein - Page 200
Outputting Rows Iteratively

CREATE FUNCTION stockpivot (p refcur_pkg.refcur_t)
RETURN tickertypeset Define as
PIPELINED PIPELINED
IS
out_rec tickertype := tickertype (NULL, NULL, NULL);
in_rec p%ROWTYPE;
BEGIN
LOOP
FETCH p INTO in_rec;
EXIT WHEN p%NOTFOUND;
out_rec.ticker := in_rec.ticker;
out_rec.pricetype := 'O';
out_rec.price := in_rec.openprice; PIPE ROW sends
PIPE ROW (out_rec); the data out.
END LOOP;
CLOSE p;
RETURN;
No RETURN of
END;
any actual data.
/ tabfunc.sql

Copyright 2000-2005 Steven Feuerstein - Page 201
Parallel Execution and
Table Functions

 Prior to Oracle9i, calling a function inside a
SQL statement caused serialization.
– The parallel query mechanism could not be used.
 Now you can enable parallel execution of a
table function.
– This greatly increases the usability of PL/SQL-
enriched SQL in data warehouse applications.

Copyright 2000-2005 Steven Feuerstein - Page 202
Enabling Parallel Execution

 The table function's parameter list must consist only
of a single strongly-typed REF CURSOR.
 Include the PARALLEL_ENABLE hint in the program
header.
– Choose a partition option that specifies how the function's
execution should be partitioned.
– "ANY" means that the results are independent of the order
in which the function receives the input rows (through the
REF CURSOR).
{[ORDER | CLUSTER] BY column_list}
PARALLEL_ENABLE ({PARTITION p BY
[ANY | (HASH | RANGE) column_list]} )

Copyright 2000-2005 Steven Feuerstein - Page 203
Examples of Parallelized Functions

CREATE OR REPLACE FUNCTION Aggregate_Xform (
p_input_rows in My_Types.cur_t) RETURN My_Types.dept_sals_tab
PIPELINED

PARALLEL_ENABLE (
with Partition p_input_rows BY ANY ) Simplest form, results don't vary
from order in which function gets
input rows.

CLUSTER P_INPUT_ROWS BY (dept)
with PARALLEL_ENABLE All rows for a given department
( PARTITION p_input_rows must go to the same slave, and
BY HASH (dept) ) rows are delivered consecutively.

ORDER p_input_rows BY (c1, c2) Rows are delivered to a particular
with PARALLEL_ENABLE slave as directed by partition...
( PARTITION p_input_rows and will be locally sorted by that
BY RANGE (c1) ) slave.
Copyright 2000-2005 Steven Feuerstein - Page 204
Miscellaneous, But Really Good Stuff

 Leveraging Java from within PL/SQL
 DBMS_UTILITY.FORMAT_ERROR_BACKTR
ACE
 Execution model options: choose
between definer and invoker rights
 CASE statement and expression
 Improvements to UTL_FILE
 Row-level security
 Other Oracle10g new features
Copyright 2000-2005 Steven Feuerstein - Page 205
Leveraging Java inside PL/SQL

 No! But we should all learn the basics
of Java and know how to take
advantage of it from within Pl/SQL.
Copyright 2000-2005 Steven Feuerstein - Page 206
Overview of Java Interoperability

 Java inside or outside 8i server can call PL/SQL
– Standard JDBC and SQLJ calls with Oracle extensions
– Same Java on client, mid-tier, or server

 PL/SQL can call Java inside 8i server
– Command-line tools load Java classes
– DDL extensions publish Java classes
– Writing stored procedures, functions, triggers in Java
– Distinct Java & PL/SQL namespaces

 But first...a BRIEF introduction to Java...
Copyright 2000-2005 Steven Feuerstein - Page 207
Some Important Things to Remember

 Java is a case sensitive language...
– string is definitely not the same as String.
 Everything is a class (or an object
instantiated from a class)...
– Before you can call a (non-static) class method,
you have to instantiate an object from that class.
– Well, everything except the primitive datatypes.
 You don't have to know how to do everything
with Java to get lots of value out of it...
– Don't get overwhelmed by all the classes and all
the strange quirks.

Copyright 2000-2005 Steven Feuerstein - Page 208
Why use Java inside Oracle?

 PL/SQL extender
– For example, better file I/O
– Clean access to operating system
functionality
 PL/SQL replacement
– More standard language
– Good performer for numeric processing
tasks
– Beware database I/O & string manipulation
performance
Copyright 2000-2005 Steven Feuerstein - Page 209
How we can leverage Java in PL/SQL

 Find or create the Java classes that fill your
requirements.
– Load those classes into the Oracle
database with loadjava or special DDL
commands.
 "Publish" the Java method from within a
PL/SQL procedure or function.
 Call the PL/SQL wrapper program to apply
the Java functionality inside PL/SQL.
Copyright 2000-2005 Steven Feuerstein - Page 210
AS LANGUAGE JAVA wrapper syntax

 Example (top-level
CREATE OR REPLACE FUNCTION hello_emp
call spec) (empno_in IN NUMBER)
RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'datacraft.bill.Hello.Emp(int)
return java.lang.String';
 Syntax (simplified) /

CREATE [ OR REPLACE ] { PROCEDURE | FUNCTION } <name>
[ RETURN <sqltype> ]
[ ( <args> ) ]
[ AUTHID { DEFINER | CURRENT_USER } ]
AS LANGUAGE JAVA
NAME '<method fullname> (<Java type fullname>, ...)
[ return <Java type fullname> ]';
Copyright 2000-2005 Steven Feuerstein - Page 211
Publishing -- more concepts

Shape mapping
– “void” methods become PL/SQL procedures
– Signature mismatches detected only at runtime
Type mapping (the most challenging)
java.lang.String VARCHAR2
java.sql.Timestamp DATE
java.math.BigDecimal NUMBER
oracle.sql.STRUCT user-defined object type
<named type> user-defined object type
oracle.sql.REF object REFerence
oracle.sql.ARRAY user-defined collection type
Copyright 2000-2005 Steven Feuerstein - Page 212
New DDL Statements and Roles

 CREATE JAVA
– Alternative to “loadjava” utility, Creates or replaces an
Oracle “library unit” from Java source, class, or resource
 ALTER JAVA
utlzip.sql
– Compiles Java source, resolves Java class references.
 DROP JAVA
– Drops a named Java library unit
 Several roles available for Java operations:
– JAVAUSERPRIV (read I/O operations) and JAVASYSPRIV
(write IO operations), JAVA_ADMIN, JAVAIDPRIV,
JAVADEBUGPRIV
– You can also grant specific privileges.
Copyright 2000-2005 Steven Feuerstein - Page 213
Example: Improving File I/O

 You can read/write files in PL/SQL with
UTL_FILE, but that package is very limited.
 Java offers many file-related classes with
much greater capabilities.
 Let's see how we can make that great Java
stuff available from within PL/SQL.

Copyright 2000-2005 Steven Feuerstein - Page 214
Encapsulate Java Classes

 You won't generally access native Java
methods in your PL/SQL wrapper.
– Instead build a static method that instantiates a
Java object from the class and then invokes the
relevant method against that object.
 Let's start with something simple...
– The File class offers a length method that
returns the number of bytes in a file.
– This is not available through UTL_FILE (though
you can get it through DBMS_LOB).
Copyright 2000-2005 Steven Feuerstein - Page 215
A Java Class for File Manipulation

import java.io.File;
public class JFile2 { Accept the
public static long length (String fileName) { name of a file
File myFile = new File (fileName);
return myFile.length(); }
and return the
} length.

 Take each of these steps:
– Import the File class to resolve
reference.
– Instantiate a File object for the
specified name.
JFile2.java
– Call the method of choice against that
Copyright 2000-2005 Steven Feuerstein - Page 216
Build Package over Java Method

 Let's put it in a package; we will certainly want
to add more functionality over time.
– I translate the Java long to a PL/SQL NUMBER.

CREATE OR REPLACE PACKAGE xfile
IS
FUNCTION length (file IN VARCHAR2) RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY xfile
IS
FUNCTION length (file IN VARCHAR2) RETURN NUMBER
AS LANGUAGE JAVA
NAME 'JFile.length (java.lang.String) return long';
END;
xfile2.pkg
/

Copyright 2000-2005 Steven Feuerstein - Page 217
Mapping the Boolean Datatype

 Both Java and PL/SQL support a native
Boolean datatype, so you'd expect smooth
sailing. Not so!
 To pass a Boolean back from Java to PL/SQL,
you will need to take these steps:
– 1. Convert the Java boolean to a String or number
and return that value.
– 2. Write a "hidden" PL/SQL wrapper function that
returns the string or number.
– 3. Write a "public" PL/SQL wrapper function to
convert that number to a true PL/SQL Boolean.

Copyright 2000-2005 Steven Feuerstein - Page 218
Translate Boolean to Number

 Am I allowed to read this file? For real?
import java.io.File;

public class JFile3 {
public static int canRead (String fileName) {
File myFile = new File (fileName);
boolean retval = myFile.canRead();
if (retval) return 1; else return 0; }
}

 Translate TRUE to 1 and FALSE to 0.
– And don't forget: this is a boolean
primitive, not a Boolean class.
JFile3.java

Copyright 2000-2005 Steven Feuerstein - Page 219
Wrap Pseudo-Boolean Function

 Simple translation back to PL/SQL Boolean.
– Avoid the hard-codings with named constants...
CREATE OR REPLACE PACKAGE xfile IS
FUNCTION canRead (file IN VARCHAR2) RETURN BOOLEAN;
END;
/
CREATE OR REPLACE PACKAGE BODY xfile
IS
FUNCTION IcanRead (file IN VARCHAR2) RETURN NUMBER
AS LANGUAGE JAVA
NAME 'JFile3.canRead (java.lang.String) return int';

FUNCTION canRead (file IN VARCHAR2) RETURN BOOLEAN AS
BEGIN xfile3.pkg
RETURN IcanRead (file) = 1; JFile4.java
END; xfile4.pkg
END; JFile.java
xfile.pkg
Copyright 2000-2005 Steven Feuerstein - Page 220
Passing Collections to Java

 Let's take a look at what is needed to move a
PL/SQL collection to a Java array.
– A utility that deletes all the files found in the specified
directories (one per row in the collection) that have not
been modified since the specified date.
CREATE or REPLACE JAVA SOURCE NAMED "DeleteFile" AS

import java.io.*;
import java.sql.*;
import oracle.jdbc.driver.*; I need the SQL and date-related
import oracle.sql.*; packages, so I specify them in
import java.util.Date; my import list.
import java.text.*;
import java.text.DateFormat.*;

Copyright 2000-2005 Steven Feuerstein - Page 221 DeleteFile.java
Passing Collections to Java, continued.

 Use oracle.sql.ARRAY to pass in the collection. Cast
to STRUCT to extract the individual attribute values
(name of file and timestamp).
public class DeleteFile {
public static int delete (oracle.sql.ARRAY tbl)
throws SQLException {
try {
ResultSet rs = tbl.getResultSet();
for (int ndx = 0; ndx < tbl.length(); ndx++) {
rs.next();
int aryndx = (int)rs.getInt(1);

STRUCT obj = (STRUCT)rs.getObject(2);
Object[] attrs = obj.getAttributes();

String fileDir = (String)attrs[0];
Timestamp saveDate = (java.sql.Timestamp)attrs[1];
...
Copyright 2000-2005 Steven Feuerstein - Page 222
}
Some Other Cool Extensions

Courtesy of Vadim Loevski

 Zip files from within PL/SQL.

FileOutputStream fout = new FileOutputStream(outfilename);
ZipOutputStream zout = new ZipOutputStream(fout);
ZipEntry ze = new ZipEntry((new File(infilename)).getName());

 Execute any operating system command
Runtime rt = java.lang.Runtime.getRuntime(); utlzip.sql
utlcmd.sql

Copyright 2000-2005 Steven Feuerstein - Page 223
Viewing Java Output

 Java provides a "print line" method:
System.out.println
– Call it within methods and output will display in your
Java environment...but what if you are running this
code from within a PL/SQL wrapper?
System.out.println (my_object)

 Redirect the output to the DBMS_OUTPUT
buffer.
SET SERVEROUTPUT ON SIZE 1000000
CALL DBMS_JAVA.SET_OUTPUT (1000000);

Copyright 2000-2005 Steven Feuerstein - Page 224
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE

Long-standing challenge in PL/SQL:

How can I find the line number on which an
error was raised in PL/SQL?

 Before Oracle10g, the only way is to let the
error go unhandled in your PL/SQL code!
 DBMS_UTILITY.FORMAT_ERROR_STACK
only gives you the full error message.
– And is recommended by Oracle in place of
SQLERRM. But in Oracle10g, we have "back trace"!
Copyright 2000-2005 Steven Feuerstein - Page 225
Letting the error go unhandled…

CREATE OR REPLACE PROCEDURE proc1 IS
BEGIN
DBMS_OUTPUT.put_line ('running proc1');
RAISE NO_DATA_FOUND;
END;
/
CREATE OR REPLACE PROCEDURE proc2 IS
l_str VARCHAR2(30)
:= 'calling proc1';
BEGIN
DBMS_OUTPUT.put_line (l_str);
proc1;
END;
/
CREATE OR REPLACE PROCEDURE proc3 IS ERROR at line 1:
BEGIN ORA-01403: no data found
DBMS_OUTPUT.put_line ('calling proc2'); ORA-06512: at "SCOTT.PROC1", line 7
proc2; ORA-06512: at "SCOTT.PROC2", line 8
END; ORA-06512: at "SCOTT.PROC3", line 5
/ ORA-06512: at line 3

Backtrace.sql
Copyright 2000-2005 Steven Feuerstein - Page 226
Displaying the “error stack” inside PL/SQL

CREATE OR REPLACE PROCEDURE proc3
IS
BEGIN
DBMS_OUTPUT.put_line ('calling proc2');
proc2;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line (
DBMS_UTILITY.FORMAT_ERROR_STACK);
END;
/

SQL> exec proc3
calling proc2
calling proc1
running proc1
backtrace.sql ORA-01403: no data found

Copyright 2000-2005 Steven Feuerstein - Page 227
Displaying the contents of BACKTRACE

CREATE OR REPLACE PROCEDURE proc3
IS
BEGIN
DBMS_OUTPUT.put_line ('calling proc2');
proc2;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error stack at top level:');
DBMS_OUTPUT.put_line (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;
/

SQL> exec proc3
calling proc2
calling proc1
running proc1
Error stack at top level:
ORA-06512: at "SCOTT.PROC1", line 5
ORA-06512: at "SCOTT.PROC2", line 7
ORA-06512: at "SCOTT.PROC3", line 5

Copyright 2000-2005 Steven Feuerstein - Page 228 bt.pkg
The BACKTRACE stack with re-RAISEs

CREATE OR REPLACE PROCEDURE proc1 IS
BEGIN
… SQL> exec proc3
EXCEPTION calling proc2
WHEN OTHERS THEN calling proc1
DBMS_OUTPUT.put_line ( running proc1
'Error stack in block where raised:'); Error stack in block where raised:
DBMS_OUTPUT.put_line ( ORA-06512: at "SCOTT.PROC1", line 5
DBMS_UTILITY.format_error_backtrace);
RAISE; Error stack at top level:
END; ORA-06512: at "SCOTT.PROC1", line
/ 11
CREATE OR REPLACE PROCEDURE proc3 IS ORA-06512: at "SCOTT.PROC2", line 7
BEGIN ORA-06512: at "SCOTT.PROC3", line 5
DBMS_OUTPUT.put_line ('calling proc2');
proc2; Program owner = SCOTT
EXCEPTION Program name = PROC1
WHEN OTHERS Line number = 11
THEN
DBMS_OUTPUT.put_line ('Error stack at top level:');
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_backtrace);
bt.show (DBMS_UTILITY.format_error_backtrace);END;
/
Copyright 2000-2005 Steven Feuerstein - Page 229
The BACKTRACE stack with new RAISE

CREATE OR REPLACE PROCEDURE proc1
IS
BEGIN
DBMS_OUTPUT.put_line ('running proc1');
RAISE NO_DATA_FOUND;
EXCEPTION SQL> exec proc3
WHEN OTHERS calling proc2
THEN calling proc1
DBMS_OUTPUT.put_line ('Error stack in block where
running
raised:');
proc1
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_backtrace);
Error stack in block where raised:
RAISE; ORA-06512: at "SCOTT.PROC1", line 5
END;
/ Error stack at top level:
ORA-06512: at "SCOTT.PROC2", line 9
CREATE OR REPLACE PROCEDURE proc2 ORA-06512: at "SCOTT.PROC3", line 5
IS
BEGIN Program owner = SCOTT
DBMS_OUTPUT.put_line ('calling proc1'); Program name = PROC2
proc1; Line number = 9
EXCEPTION
WHEN OTHERS
THEN
RAISE VALUE_ERROR;
END;
Copyright
/ 2000-2005 Steven Feuerstein - Page 230
Execution Model Options

Execution Model Options
for Oracle8i PL/SQL:

Invoker and Definer Rights

Copyright 2000-2005 Steven Feuerstein - Page 231
Some background...

 Prior to Oracle8i, whenever you executed a stored
program, it ran under the privileges of the account
in which the program was defined.
– This is called the …
Definer Rights Model

 With Oracle8i, you can now decide at compilation
time whether your program or package will execute
in the definer's schema (the default) or the schema
of the invoker of the code.
– This is called the … Invoker Rights Model
Copyright 2000-2005 Steven Feuerstein - Page 232
About Definer Rights
OE Code
 Allows you to Sam_Sales
Order_Mgt
centralize access to Place
Close Old
and control of Cancel Orders
underlying data
structures.
 Ignores roles and
OE Data X
Cannot alter
relies on directly- Orders table directly.
granted privileges.
 But it can be a source
of confusion and Note: Oracle built-in packages have
architectural long had the capability of running
under the invoker's authority.
problems.
Copyright 2000-2005 Steven Feuerstein - Page 233
Problems with Definer Rights

 Deployment & maintenance
– Must install module in all remote databases where needed
– In some databases, each user has own copy of table(s),
requiring copy of stored module
 Security
– No declarative way to restrict privileges on certain modules in
a package -- it's all or nothing, unless you write code in the
package to essentially recreate roles programmatically.
– Difficult to audit privileges
 Sure would be nice to have a choice...and now you do!

Copyright 2000-2005 Steven Feuerstein - Page 234
Oracle8i Invoker Rights

 For top level modules:

CREATE [ OR REPLACE ] <module type>
[ AUTHID { DEFINER | CURRENT_USER } ]
AS ...

 For modules with separate spec and body,
AUTHID goes only in spec, and must be at
the package level.
– Holds true for packages and object types.

Copyright 2000-2005 Steven Feuerstein - Page 235
"Reflection" Capability of Invoker Rights

 With invoker rights, you can execute code owned
by another schema, yet have all references to data
structures "reflect back" into your own schema.

Central Code schema
User/Data schema
PACKAGE acct_mgr PROCEDURE mng_account IS
make BEGIN
AUTHID modify ...
code.acct_mgr.destroy(...);
CURRENT_USER destroy END;

...FROM accounts
accounts table
WHERE...

Copyright 2000-2005 Steven Feuerstein - Page 236
When Invoker Rights Applies

 Resolution against invoker's privileges is made
for these statements:
– SELECT, INSERT, UPDATE, and DELETE data
manipulation statements
– The LOCK TABLE transaction control statement
– OPEN and OPEN-FOR cursor control statements
– EXECUTE IMMEDIATE and OPEN-FOR-USING dynamic
SQL statements
– SQL statements parsed using DBMS_SQL.PARSE()
 For all other statements, resolution is by the
owner's privileges.
– This includes ALL code references.
Copyright 2000-2005 Steven Feuerstein - Page 237
Roles and Privileges

 With definer rights, roles are disabled and
ignored.
– All references are resolved against directly granted
privileges.
 With invoker rights, roles are enabled and
used for privilege checking.
– You can even use dynamic SQL to set roles for the
session, altering how the reference is resolved at
run-time.
– Exception: if the CURRENT_USER programs was
called directly or indirectly by a definer-rights
subprogram.
invrole.sql
Copyright 2000-2005 Steven Feuerstein - Page 238
Accessing a table through roles

invoker_scott_emp
SYSTEM
SCOTT
emp_
emp_ through_
through_ role
role

showCount
invoker_system_emp DEMO

Copyright 2000-2005 Steven Feuerstein - Page 239
PL/SQL now uses roles!
DECLARE
PROCEDURE setrole (role_in IN VARCHAR2) IS
BEGIN
Change the DBMS_OUTPUT.put_line ( 'Set role to '
current role || role_in);
and view SYS.DBMS_SESSION.set_role (role_in);
tables. scott.showcount; scott.showcount (TRUE);
END;
BEGIN
setrole ('invoker_system_emp');
setrole ('invoker_scott_emp');
END;

The output...
Set role to invoker_system_emp
Behavior count of emp_thru_role = 1
Error counting emp_thru_role =
changes with ORA-00942: table or view does not exist
the role
Set role to invoker_scott_emp
Error counting emp_thru_role =
Copyright 2000-2005 Steven Feuerstein - Page 240 ORA-00942: table or view does not exist
count of scott.emp_thru_role = 14
Compiling with "Template" Objects

 If you are writing code with the intention of relying on
invoker rights, the data object referenced may not be
present in the code's schema.
– You need some kind of "template" against which to
successfully compile the code.
 Two options:
– Create a synonym to any of the possible resolved objects.
– Create a local, "dummy" object to allow the code to
compile, knowing that it will never be used at run-time.

Copyright 2000-2005 Steven Feuerstein - Page 241
Invoker Rights and Dynamic SQL

 If you create stored programs/utilities that
rely on dynamic SQL, you should always
declare them to use invokers rights.
 Otherwise, the requested action may be taken
against the DEFINER's schema (or some other
schema) and not your own schema.
CREATE OR REPLACE PROCEDURE runddl (
ddl_in in VARCHAR2)
AUTHID CURRENT_USER
IS
BEGIN
EXECUTE IMMEDIATE ddl_in;
END;

Copyright 2000-2005 Steven Feuerstein - Page 242
Invoker-Definer Precedence

 If the first program in the execution stack is defined
with invoker rights, then it executes under the session
user's authority.
 When and if a definer rights program is called in the
stack, the "current user" is set to the owner of the
definer rights program.
 All subsequent calls in the stack are resolved
according to the privileges of that schema.

invdefinv.sql
invdefinv.tst
irdynsql.sql
Copyright 2000-2005 Steven Feuerstein - Page 243
Invoker Rights and Distributed
Databases
 Invoker rights affect only one kind of database link--
current-user links, which are created as follows:
CREATE DATABASE LINK link_name CONNECT TO
CURRENT_USER USING connect_string;

 A current-user link lets you connect to a remote
database as another user, with that user's privileges.
 To connect, Oracle uses the username of the current
user (who must be a global user).
– Suppose an invoker-rights subprogram owned by user
blake references the database link below. If global
user scott calls the subprogram, it connects to the
Dallas database as user scott, who is the current
user.Steven Feuerstein - Page 244
Copyright 2000-2005
Oracle9i Release 2 New and Improved UTL_FILE

 With UTL_FILE, you can now:
– UTL_FILE.FREMOVE - Remove a file
– UTL_FILE.FRENAME - Rename a file, and also in effect
move files
– UTL_FILE.FCOPY - Copy all or part of one file to another
– UTL_FILE.FGETATTR - Retrieves attributes of the file,
such as its length
 You can also use a database DIRECTORY to
specify the location of the file; UTL_FILE_DIR will
be ignored!!!!

Copyright 2000-2005 Steven Feuerstein - Page 245
Working with Database Directories

 A Directory is a database "object", define with
a CREATE statement.
– You need CREATE ANY DIRECTORY
privilege.
CREATE OR REPLACE DIRECTORY ERROR_LOG AS '/tmp/apps/log';

View those directories SELECT owner, directory_name, directory_path
to which you have
access.
FROM ALL_DIRECTORIES;

Grant READ or WRITE GRANT READ ON DIRECTORY error_log TO SCOTT;
privileges on a
directory.
utlfile_92.sql
Copyright 2000-2005 Steven Feuerstein - Page 246
Copy a File

DECLARE
file_suffix VARCHAR2 (100)
:= TO_CHAR (SYSDATE, 'YYYYMMDDHHMISS');
BEGIN
-- Copy the entire file...
UTL_FILE.fcopy (
src_location => 'DEVELOPMENT_DIR',
src_filename => 'archive.zip',
dest_location => 'ARCHIVE_DIR',
dest_filename => 'archive'
|| file_suffix fcopy.sql
|| '.zip' fileIO92.pkg
);
END;

 You can specify an OS directory or a database object
of type DIRECTORY (as shown above)
Copyright 2000-2005 Steven Feuerstein - Page 247
Remove a File

BEGIN
UTL_FILE.fremove (
src_location => 'DEVELOPMENT_DIR',
src_filename => 'archive.zip'
);
EXCEPTION
-- If you call FREMOVE, you should check explicitly
-- for deletion failures.
WHEN UTL_FILE.delete_failed
THEN
... Deal with failure to remove
END;

 If no error is raised, then you deleted successfully

fremove.sql
fileIO92.pkg
Copyright 2000-2005 Steven Feuerstein - Page 248
Rename/move a File

DECLARE
file_suffix VARCHAR2 (100) := TO_CHAR (SYSDATE, 'YYYYMMDD');
BEGIN
-- Rename/move the entire file in a single step.
UTL_FILE.frename (
src_location => 'DEVELOPMENT_DIR',
src_filename => 'archive.zip',
dest_location => 'ARCHIVE_DIR',
dest_filename => 'archive' || file_suffix || '.zip',
overwrite => FALSE
);
EXCEPTION
WHEN UTL_FILE.rename_failed
frename.sql
THEN fileIO92.pkg
... Deal with failure to rename
END;

 You specify target location and file name
Copyright 2000-2005 Steven Feuerstein - Page 249
Obtaining attributes of a file

CREATE OR REPLACE FUNCTION flength (
location_in
file_in
IN
IN
VARCHAR2,
VARCHAR2
 How big is a file? What
)
RETURN PLS_INTEGER
is its block size? Does
IS the file exist?
TYPE fgetattr_t IS RECORD (
fexists BOOLEAN,  All valuable questions.
file_length PLS_INTEGER,
block_size PLS_INTEGER  All answered with a call
);
to UTL_FILE.FGETATTR.
fgetattr_rec fgetattr_t;
BEGIN
UTL_FILE.fgetattr (
location => location_in,
filename => file_in,
fexists => fgetattr_rec.fexists,
file_length => fgetattr_rec.file_length,
block_size => fgetattr_rec.block_size flength.sql
); fileIO92.pkg
RETURN fgetattr_rec.file_length;
END flength;
Copyright 2000-2005 Steven Feuerstein - Page 250
Encapsulate and Improve

 The best way to take advantage of the new UTL_FILE
features is to encapsulate or wrap them inside a layer
of enhancing code.
– Improve the error handling and reporting.
– Hide the messy details of workarounds/patches.
– Provide easy backups of files.
– Higher level programs like "change the extension of my file"

fileIO92.pkg

Copyright 2000-2005 Steven Feuerstein - Page 251
Row-Level Security

Row-Level Security

Also known as:
Fine-grained Access Control
or
Virtual Private Database

Copyright 2000-2005 Steven Feuerstein - Page 252
Row-Level Security

 Oracle8i offers a new package, DBMS_RLS,
with which to implement automated row-level
security
– Establish security "policies" on (restricted access
to) individual rows of a table.
– Prior to Oracle8i, you could achieve this only
partially through the use of views.
– The DBMS_RLS package (along with "system
contexts") now allow you to do so in a foolproof
manner.
Copyright 2000-2005 Steven Feuerstein - Page 253
RLS Architecture

Copyright 2000-2005 Steven Feuerstein - Page 254
Benefits of RLS

 Easier to maintain applications
– Replace a complex array of views with a single
table and single supporting "security policy"
package.
 Security and productivity strengthened
– Move rules and security issues out of the hands of
the application developer.
 Applications can evolve more responsively
– As rules governing access to data changes, those
changes can be applied more quickly to the data
and code base.
Copyright 2000-2005 Steven Feuerstein - Page 255
Components Needed for RLS

 System Contexts
– A new feature in Oracle8i, the system context is a named set
of attribute-value pairs global to your session.
 Security Policy Packages
– Write a package containing functions that establish the
security policies to be applied to a particular table.
 Database Logon Triggers
– I want to make sure that when a person logs in, their context
information is set properly.
 DBMS_RLS
– Use programs in DBMS_RLS to associate your security
policies with tables.
Copyright 2000-2005 Steven Feuerstein - Page 256
Privileges Needed for RLS

 EXECUTE_CATALOG_ROLE.
– Allows the developer to execute the DBMS_RLS
package. Alternatively, you may just grant
execute on DBMS_RLS to the account when
connected as SYS.
 CREATE ANY CONTEXT
– Allows the developer to create application
contexts.

Copyright 2000-2005 Steven Feuerstein - Page 257
Example: A National Health Care System

 The year is 2010.
– A massive, popular uprising in the United States has forced the
establishment of a national healthcare system. No more for-profit
hospitals pulling billions of dollars out of the system; no more private
insurance companies soaking up 30 cents on the dollar; all children are
vaccinated; all pregnant women receive excellent pre-natal care.
 We need a top-notch, highly secure database for
NHCS; tables are patient, doctor, clinic, regulator.
 Here are some rules:
fgac.sql
– Doctors can only see patients who are assigned to their clinic.
– Regulators can only see patients who reside in the same state.
– Patients can only see information about themselves.

Copyright 2000-2005 Steven Feuerstein - Page 258
Define and Set the Context
 Define a context in the database
– Associate a package with the context; now the context can
only be set by executing code from that package.
CREATE CONTEXT patient_restriction USING nhc_pkg;

 Create a procedure that will set the context
attributes, usually in a login trigger.
This is a simplification. See
PROCEDURE set_context IS fgac.sql for logic that identifies
BEGIN different types of people and
DBMS_SESSION.SET_CONTEXT ( sets the context accordingly.
'patient_restriction',
c_person_type_attr, 'DOCTOR');
DBMS_SESSION.SET_CONTEXT (
'patient_restriction', c_person_id_attr, doc_rec.doctor_id);
END;
Copyright 2000-2005 Steven Feuerstein - Page 259
Define the Predicate
 A predicate is a string that will be appended to the WHERE
clause of the table to which this security policy is associated
(see next page).

FUNCTION person_predicate (
schema_in VARCHAR2, name_in VARCHAR2) RETURN VARCHAR2
IS
l_context VARCHAR2(100) := Extract the context
SYS_CONTEXT (c_context, c_person_type_attr); information for this
retval VARCHAR2(2000); connection.
BEGIN
IF l_context = 'DOCTOR'
THEN Embed a call to
retval := 'home_clinic_id IN SYS_CONTEXT within the
(SELECT home_clinic_id FROM doctor dynamic predicate for real
WHERE doctor_id = SYS_CONTEXT (''' || time evaluation
c_context || ''', ''' ||
c_person_id_attr || '''))';
Copyright 2000-2005 Steven Feuerstein - Page 260
Guidelines for Predicates

 In almost every case, you want your
predicates to return consistent values for the
duration of a session.
– Otherwise, you get very unpredictable results.
– Set up your policy package so that context
values are set just once during a session.
– Embed calls to SYS_CONTEXT within your
dynamic predicate string so that it is evaluated
with each query. It is treated, by the way, as a
bind variable.

Copyright 2000-2005 Steven Feuerstein - Page 261
Create the Security Policy
 Use the DBMS_RLS.ADD_POLICY procedure.
– The following procedure call specifies that the WHERE
clause of any query, update or delete against the
SCOTT.PATIENT table will be modified by the string
returned by the person_predicate function of the
SCOTT.nhc_pkg package.
BEGIN
DBMS_RLS.ADD_POLICY (
OBJECT_SCHEMA => 'SCOTT',
OBJECT_NAME => 'patient',
POLICY_NAME => 'patient_privacy',
FUNCTION_SCHEMA => 'SCOTT',
POLICY_FUNCTION => 'nhc_pkg.person_predicate',
STATEMENT_TYPES => 'SELECT,UPDATE,DELETE',
UPDATE_CHECK => TRUE); Can only modify data
END; that you are allowed
to query.
Copyright 2000-2005 Steven Feuerstein - Page 262
Use Logon Trigger to Set Context

 By setting the context in the logon trigger, we guarantee that
the context is set (and predicate applied) no matter the entry
point to Oracle.

CREATE OR REPLACE TRIGGER set_id_on_logon
AFTER LOGON ON DATABASE
BEGIN
nhc_pkg.set_context;
EXCEPTION Exception handling in
WHEN OTHERS trigger is critical! If you
THEN allow an exception to go
DBMS_OUTPUT.PUT_LINE ( unhandled, logon is
'Error ' || SQLCODE || disabled.
' setting context for ' || USER');
END;

fgac.sql

Copyright 2000-2005 Steven Feuerstein - Page 263
Queries Transparently Filtered

Context Information for "SWALLACE":
Type: DOCTOR
ID: 1060
Predicate: Doctor sees only her
home_clinic_id IN patients.
(SELECT home_clinic_id FROM doctor
WHERE doctor_id = SYS_CONTEXT ('patient_restriction',
'person_id'))

Patients Visible to "SWALLACE":
CSILVA - Chris Silva - IL
VSILVA - Veva Silva - IL

Context Information for "CSILVA":
Type: PATIENT Patient sees only
ID: himself.
Predicate: schema_name = 'CSILVA'

Patients Visible to "CSILVA":
CSILVA - Chris Silva - IL
Copyright 2000-2005 Steven Feuerstein - Page 264
Other RLS Functionality

 Drop and enable existing policies.
 Obtain and display/manipulate context
information for a session.
 Pre-defined USERENV context
functionality.

droppol.sp
enblpol.sp
showcntxt.sp
showcntxt.tst
showucntxt.sql

Copyright 2000-2005 Steven Feuerstein - Page 265
Other Handy
Oracle9i and Oracle10g Features

 Oracle9i
– CASE statement and expression
 Oracle10g
– Optimizing compiler
– Compiler warnings
– Alternative quoting mechanism
– Improved support for numeric datatypes
– Regular expression support

Copyright 2000-2005 Steven Feuerstein - Page 266
CASE Statements and Expressions

 Yes! Finally! It is here: the CASE statement!
Plus a CASE expression!
CASE selector
WHEN expression1 THEN result1
WHEN expression2 THEN result2
...
WHEN expressionN THEN resultN
[ELSE resultN+1]
END; CASE
WHEN search_condition1 THEN result1
WHEN search_condition2 THEN result2
...
WHEN search_conditionN THEN resultN
[ELSE resultN+1]
END;

Copyright 2000-2005 Steven Feuerstein - Page 267
CASE Example

 Just another step DECLARE
grade CHAR(1);
towards writing appraisal VARCHAR2(20);
BEGIN
cleaner, easier to ...
appraisal :=
read and CASE grade
maintain code. WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Very Good'
WHEN 'C' THEN 'Good'
WHEN 'D' THEN 'Fair'
WHEN 'F' THEN 'Poor'
ELSE 'No such grade'
END;
...
END;

case1.sql
Copyright 2000-2005 Steven Feuerstein - Page 268
Oracle10g Wow! An optimizing compiler!

 Yes, the PL/SQL compiler now has the ability to
automatically optimize your code.
– Possible rearrangements to the code itself
(under the covers).
 You can choose the level of optimization
through the plsql_optimization_level setting:
– 2 Most aggressive, maximum possible code
transformations, biggest impact on compile
time. [default]
– 1 Smaller scale change, less impact10g_optimize_cfl.sql
on
ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 0;
compile
Copyright 2000-2005 times
Steven Feuerstein - Page 269
Optimizing compiler details

 Oracle retains optimizer settings on a module-by-
module basis. When you recompile a particular
module with non-default settings, the settings will
"stick," allowing you to recompile later using
REUSE SETTINGS. For example:
ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL = 0;

 and then:
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;

 All compiler settings for modules are available in
the USER_PLSQL_OBJECT_SETTINGS view.

Copyright 2000-2005 Steven Feuerstein - Page 270
Oracle10g Wow! Compile-time warnings!

 You can now enable compiler warnings, helping
you avoid nuisance issues in your code.
– Generally, these are not severe errors, but
potential problems with code structure or
performance.
 To use compiler warnings, you must turn them
on in
[ENABLE your| session.
| DISABLE ERROR]:[ALL|SEVERE|INFORMATIONAL|PERFORMANCE|warning_number]

REM To enable all warnings in your session execute:
ALTER SESSION SET plsql_warnings = 'enable:all‘;

REM If you want to enable warning message number 06002 and all warnings in
REM the performance category except warning number 07202, execute:
ALTER SESSION SET plsql_warnings =
'enable:06002', 'enable:performance' , 'disable:07202';

Copyright 2000-2005 Steven Feuerstein - Page 271
Compiler time warnings - example

 Check for “dead end” code….
SQL> CREATE OR REPLACE PROCEDURE dead_code IS
2 x NUMBER := 10;
3 BEGIN
4 IF x = 10 THEN
5 x := 20;
6 ELSE
7 x := 100; -- dead code
8 END IF;
9 END dead_code;
10 /

SP2-0804: Procedure created with compilation warnings

SQL> show err
Errors for PROCEDURE DEAD_CODE:

LINE/COL ERROR
-------- -----------------------------------------------------------------
7/7 PLW-06002: Unreachable code

Copyright 2000-2005 Steven Feuerstein - Page 272
Getting info about program settings

 Check the ALL_PLSQL_OBJECT_SETTINGS
for information about warnings status,
optimization level, etc.
 By the way, Oracle9i introduced
ALL_PROCEDURES, which gives you
information about the individual programs
defined in your schema.
– AUTHID, PIPELINED, DETERMINISTIC, etc.

Copyright 2000-2005 Steven Feuerstein - Page 273
Oracle10g Alternative quoting mechanism

 What do you do when you need to put a single
quote inside a literal string?
– Before Oracle10g, you could sometimes tear
your hair out.
– Now just specify an alternative quote
Literal using old
character. Literal using new Actual Value
quoting mechanism quoting mechanism
'TZ=''CDT6CST''' q'(TZ='CDT6CST')' TZ='CDT6CST'

'''' q'-'-' '

'It''s here' q'[It's here]' It's here

'''''' nq'^''^' ''

Copyright 2000-2005 Steven Feuerstein - Page 274 10g_quotes.sql
Integer and floating point data type
Oracle10g
improvements

 Oracle now uses “machine arithmetic,” rather than
C library arithmetic, for all 32-bit INTEGER types
(BINARY_INTEGER and its subtypes)
– Previously, only PLS_INTEGER was so blessed. So
now you can use whichever type you want and
not feel concerned about a performance hit.
 Support for IEEE 754 compliant floating point
numbers to both SQL and PLSQL: single
precision BINARY_FLOAT, and the double
precision BINARY_DOUBLE.
– Faster and better for scientific number crunching.
Copyright 2000-2005 Steven Feuerstein - Page 275
Oracle10g Regular expression support

 Oracle10g supports the use of regular
expressions via four new built-in functions:
REGEXP_LIKE, REGEXP_INSTR,
REGEXP_SUBSTR, and REGEXP_REPLACE.
– Available in both SQL and PL/SQL
– Gives you much more flexibility and power
in processing text, through the use of
“meta-characters.”
 Various examples on the next page.

Copyright 2000-2005 Steven Feuerstein - Page 276
Oracle10g Regular expression examples

IF REGEXP_LIKE(phone_number,'^\(?212\)?'
THEN
-- Begins with 212, optionally enclosed by parentheses
APPLY_NYC_SURCHARGE;
END IF;

-- get the leading number part of the address
-- (up to a whitespace character)

street_number :=
REGEXP_SUBSTR(address_line1, '[[:digit:]]+[:space:]');

-- change the domain part of the email addresses, by replacing
-- everything between the @ and the '.com' with new domain name

DBMS_OUTPUT.PUT_LINE (
REGEXP_REPLACE(email_address, '@.*\.com','@new_domain.com'));

Copyright 2000-2005 Steven Feuerstein - Page 277
Acknowledgements and Resources

 Very few of my ideas are truly
original. I have learned from
every one of these books and
authors – and you can, too!

Copyright 2000-2005 Steven Feuerstein - Page 278
A guide to my mentors/resources

 A Timeless Way of Building – a beautiful and deeply spiritual book on
architecture that changed the way many developers approach writing software.
 Peopleware – a classic text on the human element behind writing software.
 Refactoring – formalized techniques for improving the internals of one's code
without affect its behavior.
 Code Complete – another classic programming book covering many aspects of
code construction.
 The Cult of Information – thought-provoking analysis of some of the down-
sides of our information age.
 Patterns of Software – a book that wrestles with the realities and problems with
code reuse and design patterns.
 Extreme Programming Explained – excellent introduction to XP.
 Code and Other Laws of Cyberspace – a groundbreaking book that recasts
the role of software developers as law-writers, and questions the direction that
software is today taking us.

Copyright 2000-2005 Steven Feuerstein - Page 279
So Much to Learn...

 Don't panic -- but don't stick your head in the sand,
either.
– You won't survive as an Oracle7 or Oracle8 developer!
 On the one hand, you simply have to be conversant
with more than just PL/SQL.
– Pick up the basics of Java and XML.
 On the other hand, you can do so much more from
within PL/SQL than you could ever do before!

Copyright 2000-2005 Steven Feuerstein - Page 280