Professional Documents
Culture Documents
Oracle PL/SQL
Advanced Techniques
Oracle7 thru Oracle8i
Steven Feuerstein
www.StevenFeuerstein.com
www.Quest.com
www.OReilly.com
and contributions most excellent from Bill Pribyl and Dick Bolz
07/27/08 Copyright
PL/SQL Advanced Techniques - page 1
Objective & Outline
◆ Objective
– Expand your knowledge and awareness of important and new
features of the PL/SQL language.
◆ Outline
– Building with Packages (Oracle7+)
– PL/SQL Collections (Oracle7 and Oracle8+)
– Cursor Variables (Oracle7 and Oracle8+)
– Dynamic SQL: DBMS_SQL and Native Dynamic SQL (8i)
– Calling Java from PL/SQL (Oracle8i) and C (Oracle8)
– Oracle Advanced Queuing with DBMS_AQ (Oracle8)
– Managing Large Objects with DBMS_LOB (Oracle8)
– Other Oracle8i New Features
» Autonomous Transactions (Oracle8i)
» Invoker Rights Model (Oracle8i)
» Row Level Security: DBMS_RLS
07/27/08 Copyright
PL/SQL Advanced Techniques - page 2
Software Used in Training
◆ PL/Vision: a library of packages installed on top of PL/SQL.
– PL/Vision Lite - use it, copy, change it for free -- unless you build
software to be sold commercially.
– Active PL/SQL Knowledge Base: contains PL/Vision Professional, the
fully supported and enhanced version.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 3
Achieving PL/SQL Excellence
Building with
PL/SQL Packages
◆ Overview
◆ Initialization section
◆ Overloading
07/27/08 Copyright
PL/SQL Advanced Techniques - page 4
What is a Package?
◆ A collection of code elements, from procedures and
functions to TYPE, variable and cursor declarations.
– Single-most important structure within PL/SQL, and almost
certainly one of the most under-utilized.
– Conceptually very simple, it can take some time to fully grasp the
implications and potential of the package.
◆ The method of choice by Oracle and other software
developers for extending the PL/SQL language.
– You will find packages in the database, in Oracle Developer/2000,
in Oracle Application Server.
◆ Let’s review some of the benefits of packages.
tmr.pkg
dbparm.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 5
When to Build a Package
◆ Join physically logically-related code. custrules.pkg
insga.pkg
– Can lead to performance improvements.
– Puts more structure and organization in your body of code.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 6
Package Initialization
◆ The initialization section is a block of code at the end of
the package body that is executed once per session, the
first time any package element is referenced.
– The PL/SQL runtime engine determines when and if this code
should be run.
no Complete request
for packaged
element.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 7
Package Initialization Structure
◆ The initialization section:
– Is defined after and outside of any PACKAGE BODY pkg
programs in the package. IS
PROCEDURE proc IS
– Is not required. In fact, most BEGIN
packages you build won't have one. END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 11
Overloading in PL/SQL Built-ins
◆ PL/SQL uses overloading in many common functions.
– You just probably never gave it a thought, and took functions like
TO_CHAR and TO_DATE totally for granted.
– But Oracle couldn't offer that level of convenience without
overloading.
number_string :=
TO_CHAR_FROM_NUMBER (10000);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 12
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. 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.
◆ 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 the datatypes are in
the same family.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 13
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;
PACKAGE only_returns
IS Only difference
FUNCTION func1 (val IN VARCHAR2) RETURN DATE; is function
FUNCTION func1 (val IN VARCHAR2) RETURN VARCHAR2;
END only_returns; RETURN type.
PACKAGE param_modes
IS Only difference
PROCEDURE proc1 (val IN VARCHAR2);
is parameter
PROCEDURE proc1 (val IN OUT VARCHAR2);
END param_modes; mode.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 14
Overload Wherever Possible
◆ Supporting Many Data Combinations
– Apply the same action to different kinds or combinations of data. In
this case, the overloading does not provide a single name for
different activities, so much as providing different ways of
requesting the same activity.
– The DBMS_OUTPUT.PUT_LINE procedure illustrates this technique --
and the PL/Vision p.l substitute does an even better job.
◆ Fitting the Program to the User
– To make your code as useful as possible, you may construct different
versions of the “same” program which correspond to different
patterns of use.
PROCEDURE l
(char_in IN VARCHAR2, date_in IN DATE,
mask_in IN VARCHAR2 := 'Month DD, YYYY - HH:MI:SS PM');
END IF;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 17
Fitting the Program to the User
Writing "unnecessary" code? Time to overload!
◆ A single piece of functionality, such as "display data" or "create
a file", can be applied or needed under very different
circumstances.
◆ If you take these different circumstances into account when you
design your package specification, the user of your package can
benefit from writing less code.
– Your code is a a more natural "fit" under a variety of
requirements.
◆ In my experience, few developers are considerate enough of
their users to try to anticipate their needs.
– If you want to write software that is admired,
appreciated...and taken completely for granted, think about
the way it will be used.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 18
Creating a File for a Quick Touch
◆ Suppose a developer needs to create a file to be used as a "flag" in the
operating system.
– She doesn't care what's in it. It just needs to be present.
– Here is the code required by UTL_FILE:
DECLARE
fid UTL_FILE.FILE_TYPE;
BEGIN
fid := UTL_FILE.FOPEN ('tmp/flags', 'exists.flg', 'W');
UTL_FILE.PUT_LINE (fid, 'blah');
UTL_FILE.FCLOSE (fid);
END;
◆ In other words, you have to declare the record to hold the file handle, even
though you are simply going to close the file immediately after opening it.
◆ Of course, sometimes you will want to create a file and then perform
additional operations, so this is just the way it has to be, right? WRONG!
07/27/08 Copyright
PL/SQL Advanced Techniques - page 19
Procedure and Function Overloaded
◆ Why not overload a "create file" program so that you can pick
the one that most closely fits your situation?
◆ Consider the PLVfile package of PL/Vision.
Overloading of FCreate Use as Function
PACKAGE PLVfile DECLARE
IS fid UTL_FILE.FILE_TYPE;
/* Procedure */ BEGIN
PROCEDURE fcreate fid := PLVfile.fcreate
(file_in IN VARCHAR2, ('temp.ini', v_user);
line_in IN VARCHAR2 := NULL); PLVfile.put_line
(fid, TO_CHAR (SYSDATE));
/* Function */
FUNCTION fcreate Use as Procedure
(file_in IN VARCHAR2,
line_in IN VARCHAR2 := NULL) BEGIN
RETURN UTL_FILE.FILE_TYPE; PLVfile.fcreate ('exists.flg');
END;
END PLVfile;
07/27/08 Copyright custrules.pkg
PL/SQL Advanced Techniques - page 20
"By-Type" Overloading
◆ In some situations, the user does not need to pass data,
but the type of data.
– For example, when you use DBMS_SQL to set up a dynamic
query, you must call the DEFINE_COLUMN procedure to define
the datatype of the Nth column in the cursor.
◆ There are three ways to accomplish this:
– Don't overload. Define a different program name for each
datatype.
– Pass a string “name” of the datatype.
– Pass a piece of data of the right type.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 21
Options for Specifying Column Type
◆ Don't even bother overloading... So many program
BEGIN names to remember!
DBMS_SQL.DEFINE_INTEGER_COLUMN (cur, 1);
DBMS_SQL.DEFINE_VARCHAR2_COLUMN (cur, 2, 30);
BEGIN
DBMS_SQL.DEFINE_COLUMN (cur, 1, DBMS_UTILITY.GET_TIME);
DBMS_SQL.DEFINE_COLUMN (cur, 2, USER, 30);
BEGIN
DBMS_SQL.DEFINE_COLUMN (cur, 1, v_empno);
DBMS_SQL.DEFINE_COLUMN (cur, 2, v_ename, 30);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 23
Generating Functions by Value
◆ In PLVgen, the user indicates the type of function to be generated
by providing a value.
– The particular value itself is of no importance. Any number, any
date, any string, any Boolean will do.
PACKAGE PLVgen
IS
PROCEDURE func (name_in IN VARCHAR2, type_in IN VARCHAR2);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 24
The Frustrations of Overloading
◆ Watch out! An overloading can compile successfully, but
you might later found out that you cannot actually call any
of the overloaded programs.
PACKAGE profits
IS
PROCEDURE calc (comp_id_IN IN NUMBER);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 25
Quiz! Nuances of Overloading
PACKAGE sales
IS
PROCEDURE calc_total (zone_in IN VARCHAR2);
END sales;
◆ Can I overload two programs which have parameters
that differ only by name, like calc_totals shown above?
– If not, why not?
– If so, how would you do it? (Don't peek at the next page!)
BEGIN
sales.calc_total ('NORTHWEST');
sales.pkg
?
sales.calc_total ('ZONE2');
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 26
Using Named Notation
<formal parameter name> => <expression>
PL/SQL
Collections
◆ Collections are single-dimensioned lists of information.
◆ Three types of collections:
– Index-by tables (Oracle7 only, originally called PL/SQL tables)
– Nested tables (Oracle8 and above)
– Variable arrays (VARRAYs, Oracle8 and above)
07/27/08 Copyright
PL/SQL Advanced Techniques - page 28
When to Use Collections
◆ Maintain any kind of list of related information for use in
your programs.
DECLARE
TYPE inmem_emp_t IS TABLE OF emp%ROWTYPE
INDEX BY BINARY_INTEGER;
emp_copy inmem_emp_t;
Error: datemgr.pkg
NO_DATA_FOUND
07/27/08 Copyright
PL/SQL Advanced Techniques - page 31
Nested Tables
[CREATE OR REPLACE] TYPE <table_type> IS
TABLE OF <datatype> [NOT NULL];
DECLARE
TYPE when_t IS TABLE OF DATE;
birthdays when_t;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 32
Nested Tables
CREATE OR REPLACE
TYPE child_table_type IS TABLE OF VARCHAR2 (30);
db_family
Barbara Anne
surname kids Gary Richard
BOLZ Lisa Marie
BOND
Eric Thomas
Max Richard
ntdemo.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 33
Variable Arrays
[CREATE OR REPLACE] TYPE <table_type> IS
VARRAY (N) OF <datatype> [NOT NULL];
DECLARE
TYPE numbers_t IS VARRAY (10) OF NUMBER;
salaries numbers_t;
CREATE OR REPLACE
TYPE child_va_type IS VARRAY (8) OF VARCHAR2 (30);
db_family
surname kids
1 Barbara Anne
BOLZ 2 Gary Richard
3 Lisa Marie
1 Eric Thomas
BOND
2 Max Richard
vademo.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 35
Defining Collections
◆ First, you define the TYPE of the collection.
– For index-by tables, 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 an instance of that type, a collection, from
the TYPE.
– You can declare multiple collections from that TYPE.
CREATE OR REPLACE PACKAGE tabtypes
IS
TYPE integer_ibt IS TABLE OF INTEGER INDEX BY BINARY_INTEGER;
TYPE integer_nt IS TABLE OF INTEGER;
TYPE integer_vat IS VARRAY(10) OF INTEGER;
...
END tabtypes;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 36
Obtaining Collection Information
SELECT A.attr_name || ' - ' || A.attr_type_name Attributes
FROM all_coll_types T, all_type_attrs A
WHERE T.owner = USER
AND T.owner = A.owner
AND T.type_name IN ('NAMES_VT', 'TMRS_VT')
AND T.elem_type_name = A.type_name;
◆ ALL_COLL_TYPES
– The types you have created (or have access to) in the database
◆ ALL_TYPE_ATTRS
– Attributes of the data type used in the TYPE definition.
– The code used to define the collection TYPE
07/27/08 Copyright
PL/SQL Advanced Techniques - page 37
Initializing Collections
◆ Before you can use a collection, it must be initialized.
– Index-by tables are initialized automatically, empty when declared.
– Nested tables and VARRAYs are atomically null. You must initialize
them explicitly with a constructor.
DECLARE
TYPE numbers_t IS VARRAY (10) OF NUMBER; TYPE defined in
salaries numbers_t := numbers_t (100, 200, 300); PL/SQL
BEGIN
07/27/08 Copyright
PL/SQL Advanced Techniques - page 38
Collections of Composites
◆ Starting with Oracle 7.3, the “homogeneous” contents of an
index-by table's row can be a record .
– Can easily create an index-by table with the same structure as a
database table by declaring a record with %ROWTYPE.
◆ Starting with Oracle8, the datatype for any of the collection types
can also be an object.
– But you cannot have nested composite datatypes.
DECLARE
TYPE comp_rectype IS RECORD
(comp_id company.company_id%TYPE, total_rev NUMBER);
BEGIN
-- Delete all rows
myCollection.DELETE;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 45
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; -- birthdays.LAST
BEGIN
LOOP
EXIT WHEN rowind IS NULL;
DBMS_OUTPUT.PUT_LINE
(birthdays(rowind).best_present);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 46
Using Collections Inside SQL
◆ Nested tables and VARRAYs can be defined as columns of a
table and referenced directly within SQL.
◆ You can also apply SQL operations to the contents of
nested tables and VARRAYs with these operators:
– THE - Maps a single column value in a single row to a virtual
database table
– 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
07/27/08 Copyright
PL/SQL Advanced Techniques - page 47
Using Collections inside SQL
SELECT column_value
FROM TABLE (SELECT children FROM db_family
WHERE surname = 'BOLZ');
db_family
Barbara Anne
surname children Gary Richard column_value
BOLZ Lisa Marie Barbara Anne
Gary Richard
BOND Lisa Marie
Eric Thomas
Max Richard
UPDATE TABLE
(SELECT children FROM db_family WHERE SURNAME = 'BOLZ)
SET column_value = 'Lisa Nadezhka'
WHERE column_value = 'Lisa Marie');
db_family
Barbara Anne
surname children Gary Richard
BOLZ Lisa Nadezhka
...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 48
Using the THE Operator
◆ Use THE to manipulate (retrieve, INSERT, UPDATE, DELETE)
contents of a nested table in a database table.
– Can only use with nested tables, not VARRAYs or index-by tables.
– Only accessible from within SQL statements in PL/SQL.
CREATE TYPE action_list_t IS TABLE OF VARCHAR2(100);
/
CREATE TABLE inflation_beater (
focus_area VARCHAR2(100),
activities action_list_t)
NESTED TABLE activities STORE AS activities_tab;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 51
Referencing IB Tables inside SQL
◆ You can't directly reference an index-by table's contents
inside SQL.
◆ Instead, call functions that retrieve the table's data, but hide
the index-by table structure.
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;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 53
Bi-Directional Cursor Emulation
◆ Oracle does not yet support the ability to move back and
forth (and at random) through a cursor's result set.
– A talked-about feature for Oracle9i -- nope, didn't make it!
◆ Instead, deposit your data in a collection and then provide
programs to access that data in the necessary fashion.
◆ This is particularly useful (read: efficient) when you need to
perform multiple passes against the data.
Notice that the
CREATE OR REPLACE PACKAGE bidir collection itself is
IS hidden.
/* Iterate through rows in the result set */
PROCEDURE setRow (nth IN PLS_INTEGER);
FUNCTION getRow RETURN employee_plus%ROWTYPE;
PROCEDURE nextRow; bidir.pkg
bidir.tst
PROCEDURE prevRow;
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 54
The Mutating Table Problem
◆ Database triggers can be attached to the SQL statement
and/or the individual row operations on a table.
Statement Level
UPDATE emp SET sal = 1000
UPDATE row 1
Writes to list
Nth row trigger fires
Process data
in the list.
Statement Trigger
07/27/08 Copyright
PL/SQL Advanced Techniques - page 56
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.
ranking.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 58
The Ranking Package
PACKAGE rank
IS
PROCEDURE add_dept (dept_id_in IN INTEGER);
PROCEDURE rank_depts;
END rank;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 59
The Ranking Package, Continued
PROCEDURE rank_depts
IS
v_deptid PLS_INTEGER := dept_tab.FIRST;
BEGIN
Avoid recursive
IF NOT in_process execution of logic
THEN
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.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 60
Which Collection Type Should I Use?
◆ Index-by tables
– Need to use in both Oracle7 and Oracle8 applications
– Want to take advantage of sparse nature for "intelligent keys".
◆ Nested tables
– You want to store large amounts of persistent data in a column.
– You want to use inside SQL.
◆ VARRAYs
– You want to preserve the order in which elements are stored.
– Set of data is relatively small (avoid row chaining).
– You want to use inside SQL.
– You don't want to have to worry about sparseness.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 61
Tips for Using Collections
◆ Wrap access to your collections.
– In many cases, you will want to avoid direct access to (assigning
and retrieving) rows in your collections.
– This will give you the flexibility to change your implementation.
– You can also hide complex rules for setting the row number.
◆ Get creative!
– Don't always fill and use the index-by table sequentially.
– If you can somehow translate your application data to an integer, it
can be used as a row number, and therefore offers indexed access.
– Julian date formats and DBMS_UTILITY.GET_HASH_VALUE offer
two different methods.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 62
Achieving PL/SQL Excellence
Cursor Variables
07/27/08 Copyright
PL/SQL Advanced Techniques - page 63
Architecture of Cursor Variables
With Hard-Coded Cursors With Cursor Variables
Shared
Result Cursor
Set Global Object
Result
Set
Area
07/27/08 Copyright
PL/SQL Advanced Techniques - page 65
Cursor Variable Example
DECLARE Declare a variable
TYPE company_curtype cursor TYPE.
IS
REF CURSOR RETURN company%ROWTYPE;
Declare cursor variable
based on that type.
company_curvar company_curtype;
Declare a record
company_rec company_curvar%ROWTYPE; from cursor variable.
BEGIN
OPEN cursor variable,
specifying the query.
OPEN company_curvar FOR
SELECT * FROM company;
FETCH from the
cursor variable.
FETCH company_curvar INTO company_rec;
Close the
cursor variable.
CLOSE company_curvar;
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 66
Explicit Cursors and Cursor Variables
◆ Both hard-coded cursors and cursor variables work with
static SQL.
– The SQL is fixed at compile-time.
– The difference is that with cursor variables, you get to decide which
static query is opened.
◆ Many cursor operations are the same:
– Close a variable cursor with the same syntax as that for static
cursors.
– Use cursor attributes (%ISOPEN, %FOUND, %NOTFOUND,
%ROWCOUNT) with cursor variables (as of Release 2.3).
– Fetch data from the cursor result set through a cursor variable with
the same syntax as that of static cursors.
◆ Let’s focus on the new and different capabilities of cursor
variables.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 67
Declaring Cursor Types and Variables
◆ Cursors are declared in two steps, just like programmer-
defined records and PL/SQL tables.
– 1. Define a cursor TYPE -- either "weak" or "strong".
– 2. Define a cursor variable.
Declare a WEAK
DECLARE referenced cursor TYPE.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 68
Strong vs. Weak Cursor Types
◆ A strong (or constrained) cursor type has a defined
return data specification.
– Can only reference cursor objects which return the same data
specification, which can be any single SQL datatype or any
previously defined record structure.
– Datatype mismatches are identified at compile time.
TYPE cur_typ_name IS REF CURSOR [ RETURN return_type ];
07/27/08 Copyright
PL/SQL Advanced Techniques - page 69
Opening with the Cursor Variable
OPEN cursor_name FOR select_statement;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 70
Opening with Strong Cursor Types
DECLARE
BEGIN
OPEN emp_curvar FOR SELECT * from emp;
...
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 71
Opening with Weak Cursor Types
PACKAGE pkg IS REF TYPE placed in
TYPE cv_type IS REF CURSOR; package so that it
END; is "globally" available.
mismatch.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 73
When to Use Cursor Variables
◆ Make it easier for calling programs (especially non-PL/SQL
programs) to manipulate result sets.
– JDBC recognizes cursor variables.
hccursor.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 74
Consolidating Different Cursors
◆ The following package specification hides the SQL behind a single open
function.
– It also creates the data structures you will need to call the function.
allcurrs.pkg
allcurs.tst
explcv.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 75
Consolidating Different Cursors
◆ The open function simply opens FOR a different SELECT based on the
criteria passed to it.
DECLARE
cv allcurs.cv_t;
v_empno emp.empno%TYPE;
BEGIN
cv := allcurs.open (&1);
LOOP
FETCH cv INTO v_empno; "Report processor"
EXIT WHEN cv%NOTFOUND; is independent of
p.l (v_empno); the particular SELECT
END LOOP;
CLOSE cv;
END;
/
07/27/08 Copyright
PL/SQL Advanced Techniques - page 77
Cursor Variables
Let's
summarize
◆ Flexibility
– Choose which static SQL statement is executed at run-time.
Dynamic SQL
07/27/08 Copyright
PL/SQL Advanced Techniques - page 79
Dynamic SQL and PL/SQL Execution
◆ "Dynamic SQL" mean that you construct the SQL statement
or PL/SQL block at runtime and then execute it.
– Available in PL/SQL since Release 2.1 and DBMS_SQL.
– Also supported with "native dynamic SQL" in Oracle8i.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 81
Native Dynamic SQL
◆ Prior to Oracle8i, you would use the DBMS_SQL built-in
package to execute dynamic SQL.
– But this package is very complex, difficult to use, and relatively slow
(performance did improve significantly as of Oracle8).
07/27/08 Copyright
PL/SQL Advanced Techniques - page 82
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE sql-string
07/27/08 Copyright
PL/SQL Advanced Techniques - page 83
COUNT(*) For Any Table
◆ Here's a handy and simple utility based on NDS:
PROCEDURE add_profit_source (
hosp_name IN VARCHAR2,
pers IN Person,
cond IN preexisting_conditions)
IS
BEGIN
EXECUTE IMMEDIATE
'INSERT INTO ' || tabname (hosp_name) ||
' VALUES (:revenue_generator, :revenue_inhibitors)'
USING pers, cond;
health$.pkg
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 86
Multiple Row Queries and NDS
◆ Oracle extends the cursor variable feature of Oracle7 to
support multi-row dynamic queries.
– Here is a simple utility the 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
TYPE cv_type IS REF CURSOR;
cv cv_type;
val VARCHAR2(32767);
BEGIN
OPEN cv FOR 'SELECT ' || col || ' FROM ' || tab ||
' WHERE ' || NVL (whr, '1 = 1');
LOOP
FETCH cv INTO val; Familiar cursor
EXIT WHEN cv%NOTFOUND; variable syntax!
DBMS_OUTPUT.PUT_LINE (val);
END LOOP;
CLOSE cv; showcol.sp
END; Copyright
07/27/08 ndsutil.pkg
PL/SQL Advanced Techniques - page 87
Some Fine Print for NDS
◆ You cannot pass schema elements (table names, column
names, etc.) through the USING clause.
◆ You cannot pass the NULL literal directly in the USING
clause. 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, then you provide a value for each placeholder (by
position).
– If dynamic PL/SQL, provide a value for each distinct placeholder (by
name).
str2list.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 88
Dynamic SQL using DBMS_SQL
◆ Prior to Oracle8i, the only way to perform dynamic SQL was
with the DBMS_SQL package.
◆ DBMS_SQL is a very large and complex package, with many
rules to follow and lots of code to write.
◆ Supports all four methods of dynamic SQL, in particular
method 4.
◆ The overhead for using DBMS_SQL has decreased
significantly in Oracle8 and again in Oracle8i.
◆ You should only use DBMS_SQL when you cannot use NDS.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 89
Learning Through Examples
◆ DDL
– Create an index from within PL/SQL
◆ DML
– Update rows in a table
◆ DML with binding
– Update rows using bind variables
◆ Queries
– Method 3 and a dynamic WHERE clause
◆ PL/SQL Version of "SELECT *"
– Example of Method 4
◆ PL/SQL
– Create a generic calculation program
07/27/08 Copyright
PL/SQL Advanced Techniques - page 90
DDL with Dynamic SQL
PROCEDURE create_index
(index_in IN VARCHAR2, tab_in IN VARCHAR2, col_in IN VARCHAR2)
IS
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
fdbk INTEGER;
DDL_statement VARCHAR2(200)
:= 'CREATE INDEX ' || index_in || ' ON ' || tab_in ||
' ( ' || col_in || ')';
BEGIN
DBMS_SQL.PARSE (cur, DDL_statement, DBMS_SQL.NATIVE);
fdbk := DBMS_SQL.EXECUTE (cur);
DBMS_SQL.CLOSE_CURSOR (cur);
END;
◆ Creates an index on any column(s) in any table in
your schema.
creind.sp – Open a cursor, which will be used to execute the DDL
statement.
– Construct the DDL statement as a string.
– Parse and execute that DDL statement.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 91
Updates with Dynamic SQL
◆ Update numeric column for specified employees.
CREATE OR REPLACE PROCEDURE updnumval (
col_in IN VARCHAR2,
ename_in IN emp.ename%TYPE,
val_in IN NUMBER)
IS
cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;
fdbk PLS_INTEGER;
BEGIN
DBMS_SQL.PARSE (cur,
'UPDATE emp SET ' || col_in || ' = ' || val_in ||
' WHERE ename LIKE UPPER (''' || ename_in || ''')',
DBMS_SQL.NATIVE);
DBMS_SQL.CLOSE_CURSOR (cur);
updnval1.sp
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 92
Updates with Bind Variables
◆ Update salaries for date range using binding.
CREATE OR REPLACE PROCEDURE updnumval (
col_in IN VARCHAR2,
start_in IN DATE, end_in IN DATE, val_in IN NUMBER)
IS
cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;
fdbk PLS_INTEGER;
BEGIN
DBMS_SQL.PARSE (cur, 'UPDATE emp SET ' ||
col_in || ' = ' || val_in ||
' WHERE hiredate BETWEEN :lodate AND :hidate', updnval2.sp
DBMS_SQL.NATIVE); updnval3.sp
07/27/08 Copyright
PL/SQL Advanced Techniques - page 94
Queries with Dynamic SQL
◆ Show employees using a dynamic WHERE clause...
CREATE OR REPLACE PROCEDURE showemps (where_in IN VARCHAR2 := NULL)
IS
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
rec emp%ROWTYPE; fdbk INTEGER;
BEGIN
DBMS_SQL.PARSE (cur, 'SELECT empno, ename FROM emp ' ||
' WHERE ' || NVL (where_in, '1=1'), DBMS_SQL.NATIVE);
DBMS_SQL.CLOSE_CURSOR (cur);
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 95
Dynamic SELECT * FROM Any Table
◆ Method 4 example: the number of columns queried changes with each
table.
– The resulting code is much more complicated. Very simplified
pseudo-code
BEGIN
FOR each-column-in-table LOOP
add-column-to-select-list;
END LOOP;
LOOP
fetch-a-row;
FOR each-column-in-table LOOP
DBMS_SQL.COLUMN_VALUE (cur, nth_col, val);
intab.sp END LOOP;
END LOOP;
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 96
Using EXECUTE_AND_FETCH
FUNCTION execute_and_fetch
(cursor_in IN INTEGER,
exact_match IN BOOLEAN DEFAULT FALSE)
RETURN INTEGER;
FUNCTION dyncalc (
oper_in IN VARCHAR2,
nargs_in IN INTEGER := 0,
arg1_in IN VARCHAR2 := NULL, arg2_in IN VARCHAR2 := NULL,
arg3_in IN VARCHAR2 := NULL, arg4_in IN VARCHAR2 := NULL,
arg5_in IN VARCHAR2 := NULL, arg6_in IN VARCHAR2 := NULL,
arg7_in IN VARCHAR2 := NULL, arg8_in IN VARCHAR2 := NULL,
arg9_in IN VARCHAR2 := NULL, arg10_in IN VARCHAR2 := NULL
)
RETURN VARCHAR2;
dyncalc.sf
dyncalc.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 98
More on Dynamic PL/SQL
BEGIN
cur := open_and_parse
('BEGIN get_max_sal (:deptin, :salout); END;');
dumplong.pkg
dumplong.tst
07/27/08 Copyright
PL/SQL Advanced Techniques - page 102
New DBMS_SQL Features
PL/SQL8
Extensions to
DBMS_SQL
07/27/08 Copyright
PL/SQL Advanced Techniques - page 103
New Features in DBMS_SQL
◆ The functionality of DBMS_SQL has been
extended in Oracle8 in several ways:
– Parse very long SQL strings
– Describe cursor columns
– Use "array processing" to perform bulk updates,
inserts, deletes and fetches.
– Support for RETURNING clause to avoid unnecessary
queries.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 104
Describing Cursor Columns
PROCEDURE DBMS_SQL.DESCRIBE_COLUMNS
(c IN INTEGER,
col_cnt OUT INTEGER,
desc_t OUT DBMS_SQL.DESC_TAB);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 105
Basic Steps to Describe Columns
◆ The following script shows the individual steps you will
need to perform in order to use this feature.
CREATE OR REPLACE PROCEDURE show_columns
IS
cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;
cols DBMS_SQL.DESC_TAB;
ncols PLS_INTEGER;
BEGIN
DBMS_SQL.PARSE (cur,
'SELECT hiredate, empno FROM emp', DBMS_SQL.NATIVE);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 106
"Array Processing" in DBMS_SQL
◆ PL/SQL8 now allows you to specify the use of "arrays", i.e.,
index tables, when you perform updates, inserts, deletes
and fetches.
◆ Instead of providing a scalar value for an operation, you
specify an index table. DBMS_SQL then repeats your action
for every row in the table.
◆ It really isn't "array processing".
– In actuality, DBMS_SQL is executing the specified SQL statement N
times, where N is the number of rows in the table.
◆ This technique still, however, can offer a significant
performance boost over Oracle7 dynamic SQL.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 107
Recommendations for Dynamic SQL
◆ Bind (vs. concatenate) whenever possible.
– Increased chance of reusing parsed SQL, and easier code to write.
– But remember: you cannot pass schema elements (table names,
column names, etc.) through the USING clause.
◆ Encapsulate statements to improve error handling.
– With NDS, only possible for statements that do not need USING and
INTO clauses, though you could write variations for those as well.
– Encapsulate DBMS_SQL.PARSE so that you include the trace in that
program.
◆ Use the Oracle8i invoker rights model whenever you want to
share your dynamic SQL programs among multiple
schemas.
– Otherwise that SQL will be executed under the authority
of the owner of the code, not the invoker of the code. effdsql.sql
openprse.pkg
whichsch.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 108
NDS or DBMS_SQL: Which is Best?
◆ Dynamic SQL and PL/SQL is very useful, but DBMS_SQL is
hard to use. Both implementations will still come in handy...
– If, of course, you have upgraded to Oracle8i!
07/27/08 Copyright
PL/SQL Advanced Techniques - page 109
Achieving PL/SQL Excellence
Oracle
Advanced Queuing
◆ The high performance, asynchronous,
persistent messaging subsytem of
Oracle
07/27/08 Copyright
PL/SQL Advanced Techniques - page 110
Who Needs Messaging? Everyone!
◆ In the distributed world of the Internet, systems are now
heavily reliant on the ability of components to communicate
with each other in a dependable, consistent manner.
Distribution
Center
Coffee Beans
Producer Retailer
Shipping
Service
Consumer
07/27/08 Copyright
PL/SQL Advanced Techniques - page 111
Applications Relying on Messaging
◆ Stock trading system
◆ Auction portals
07/27/08 Copyright
PL/SQL Advanced Techniques - page 112
Key Features of Oracle AQ
◆ Leverage full power of SQL
– Messages are stored in database tables
◆ Database high availability, scalability and reliability all carry
over to queues
– Strong history and retention
– Backup and recovery
– Comprehensive journaliing
◆ Rich message content increases usefulness of queueing
– Use object types to define highly structured payloads
◆ New to Oracle8i, AQ now offers a publish/subscribe style of
messaging between applications.
– Rule-based subscribers, message propagation, the listen feature and
notification capabilities.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 113
AQ architectural overview
Queue table
Queue
“Producers” “Consumers”
Message 4
Enqueued
messages
Message 3 Dequeued
Message 2 messages
Message1
07/27/08 Copyright
PL/SQL Advanced Techniques - page 116
DBMS_AQADM Highlights
CREATE_QUEUE_TABLE Assigns name, payload type, storage clause, sort
column, whether multiple consumers
DROP_QUEUE_TABLE Drops table if all queues in the table have been
stopped
CREATE_QUEUE Associates queue table with queue; assigns retry
and retention properties to queue
DROP_QUEUE Drops a stopped queue
START_QUEUE Can also turn on/off enqueue and dequeue
operations
STOP_QUEUE Stops queue, optionally waiting for outstanding
transactions
ADD_SUBSCRIBER Adds an “agent” as a subscriber
07/27/08 Copyright
PL/SQL Advanced Techniques - page 117
Creating Queue Tables and Queues
CREATE TYPE message_type AS OBJECT
(title VARCHAR2(30), Define the "payload"
text VARCHAR2(2000));
/
BEGIN
aq.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 119
Simple Enqueue Example
DECLARE
queueopts DBMS_AQ.ENQUEUE_OPTIONS_T; Declare records to
msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; hold various enqueue
msgid aq.msgid_type; and msg properties.
my_msg message_type;
BEGIN
my_msg :=
message_type ( Set up the payload
'First Enqueue', with an object
'May there be many more...'); constructor.
DBMS_AQ.ENQUEUE (
'msgqueue',
queueopts,
msgprops,
my_msg, Place the message on the
msgid); specified queue and get a
END; aqenq*.* msg ID in return.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 120
More Interesting Enqueue Example
DECLARE
... Same setup as previous page ...
BEGIN
my_msg := message_type (
'First Enqueue', 'May there be many more...');
my_msg := message_type (
'Second Enqueue',
'And this one goes first...');
queueopts.sequence_deviation := DBMS_AQ.BEFORE;
queueopts.relative_msgid := msgid1;
Modify the dequeue
DBMS_AQ.ENQUEUE ( sequence by changing the
'msgqueue', deviation field and relative
queueopts, msgprops, my_msg, msgid2);
msg ID.
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 121
Dequeue Example
DECLARE
queueopts DBMS_AQ.DEQUEUE_OPTIONS_T;
msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; Declare records to
msgid aq.msgid_type; hold various dequeue
/* defined in aq.pkg */ and msg properties.
my_msg message_type;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 123
Defining Message Subscribers
◆ You can specify that a message is to be enqueued for a list
of subscribers.
– The message is then not removed from the queue until all
subscribers have dequeued the message.
◆ Steps to working with a subscriber list:
– 1. The queue table must be defined to support multiple subscribers
or consumers.
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE (
queue_table => 'major_qtable',
queue_payload_type => 'student_major_t',
multiple_consumers => TRUE);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 124
Oracle AQ - Summary
◆ Very powerful and flexible architecture.
– Much more robust that DBMS_PIPE.
– Significant enhancements in Oracle8i, supporting a
publish-subscribe model, improved security, LISTEN
capability.
◆ Considered by Oracle to be a core component of its
overall solution.
– Crucial for Oracle to have a message-oriented
middleware in order to offer an all-Oracle solution.
– Should be strongly supported "down the road".
07/27/08 Copyright
PL/SQL Advanced Techniques - page 125
Achieving PL/SQL Excellence
Managing
Large Objects
with DBMS_LOB
07/27/08 Copyright
PL/SQL Advanced Techniques - page 126
LOB Terms
◆ LOB = Large OBject: a category of datatype allowing
storage of “unstructured” data up to 4 gigabytes
◆ LOB datatype can be:
– Column in table
– Attribute in object type
– Element in nested table
07/27/08 Copyright
PL/SQL Advanced Techniques - page 127
Types of Large Objects
◆ “Internal” LOBs ◆ “External” LOBs
– BLOB: unstructured binary – BFILE: pointer to an
data operating system file
– CLOB: single-byte fixed-
width character data
– NCLOB: multi-byte fixed-
width character data (or ◆ Temporary LOBs
varying width in )
– Internal LOBs that do not
participate in transactions,
improving performance.
◆ Key programming differences:
– Internal LOBs participate in transactions; external do not
– External LOBs are read-only from within Oracle
07/27/08 Copyright
PL/SQL Advanced Techniques - page 128
PL/SQL built-ins for LOBs
◆ Package DBMS_LOB
– Supports for all LOBs: Reading, substring and instring searches,
comparison and length checking
– For internal LOBs: Write, append, copy, erase, trim
– For external LOBs: File existence test, open, close
07/27/08 Copyright
PL/SQL Advanced Techniques - page 129
Deceptively simple example
◆ BLOB-typed column in a table
CREATE TABLE incoming_faxes
(fax_id INTEGER,
received DATE,
fax BLOB);
◆ Must get a new LOB locator before writing into the LOB:
DECLARE
the_url web_pages.url%TYPE := 'http://www.oodb.com';
the_loc CLOB;
BEGIN
INSERT INTO web_pages VALUES (the_url, EMPTY_CLOB())
RETURNING htmlloc INTO the_loc;
...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 132
Example: Piecewise CLOB programmatic insert
DECLARE
the_url web_pages.url%TYPE := 'http://www.oodb.com';
the_loc CLOB;
html_tab UTL_HTTP.HTML_PIECES;
piece_length PLS_INTEGER;
running_total PLS_INTEGER := 1;
BEGIN
This block retrieves INSERT INTO web_pages VALUES (the_url, EMPTY_CLOB())
and loads a web page
RETURNING htmlloc INTO the_loc;
into a CLOB column
html_tab := UTL_HTTP.REQUEST_PIECES(url => the_url);
◆ Syntax:
CREATE OR REPLACE DIRECTORY <directory name>
AS
'<full path to directory>';
07/27/08 Copyright
PL/SQL Advanced Techniques - page 135
Using BFILE datatype in DDL
◆ In a table...
◆ In an object type...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 136
The built-in BFILENAME function
◆ Returns value of datatype BFILE. Spec:
FUNCTION BFILENAME(directory_exprn, file_exprn)
RETURN BFILE;
◆ Example:
DECLARE
picture BFILE := BFILENAME('WEB_PIX', 'prodicon.gif');
BEGIN
INSERT INTO web_graphics VALUES (100015, picture);
END;
/
◆ Notes:
– No automatic file checking or synchronization
– No participation in transactions
07/27/08 Copyright
PL/SQL Advanced Techniques - page 137
Loading File into Database BLOB
CREATE TABLE web_graphic_blobs (
image_id INTEGER, image BLOB);
DECLARE
pic_file BFILE := BFILENAME('WEB_PIX', 'prodicon.gif');
pic_blob_loc BLOB := EMPTY_BLOB();
BEGIN
INSERT INTO web_graphic_blobs
VALUES (1, pic_blob_loc)
RETURNING image INTO pic_blob_loc;
DBMS_LOB.FILEOPEN(pic_file, DBMS_LOB.FILE_READONLY);
DBMS_LOB.FILECLOSE(pic_file); loadblob.sql
END;
/ Copyright
07/27/08
PL/SQL Advanced Techniques - page 138
New DBMS_LOB APIs in 8i
◆ Support for temporary LOBs
– No logging or rollback ➾ faster!
– Lifespan: session, call, or transaction
◆ Ability to retrieve LOB “chunk size”
– Allows programmer to tune READs and WRITEs
◆ For all internal LOBs
– OPEN & CLOSE allow control over timing; that can mean
less I/O
» Trigger will not fire until CLOSE
» Indexes will not update until CLOSE
– ISOPEN
07/27/08 Copyright
PL/SQL Advanced Techniques - page 139
Large Object - Summary
◆ LOB support in Oracle is now significantly HOT
improved.
– Complete support in PL/SQL (and other APIs)
– Much better performance and control than with
LONGs
– Usable in object types
◆ Some weaknesses...
– LOB locator behavior slightly abstruse
– Inability to modify BFILEs directly from within
PL/SQL.
COLD
07/27/08 Copyright
PL/SQL Advanced Techniques - page 140
Achieving PL/SQL Excellence
Leveraging Java
Inside PL/SQL
07/27/08 Copyright
PL/SQL Advanced Techniques - page 141
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
Not covered in this seminar
07/27/08 Copyright
PL/SQL Advanced Techniques - page 143
Question 2: Will Java Replace PL/SQL?
◆ While that scenario is certainly possible, it is very unlikely
and totally unthinkable for years to come.
◆ PL/SQL will still be:
– Faster and more productive than Java for database operations.
– A language in which hundreds of thousands of developers are
trained.
– Ubiquitous in thousands of production applications and millions of
lines of code.
– Supported and improved by Oracle -- and very aggressively, to boot.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 144
Some Important Things to Remember
◆ Java is a case sensitive language...
– string is definitely not the same as String.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 145
Building a Java Class
◆ Let's start from the very beginning...
public class Hello {
public static void main (String[] args) {
System.out.println ("Hello world!");
}
}
07/27/08 Copyright
PL/SQL Advanced Techniques - page 146
Compiling Classes
◆ Before you can use a class, you must compile it with the
javac command.
– You must also either have set the CLASSPATH or include it in your
javac call.
– This will convert the .java file to a .class file.
– It will also automatically recompile any classes used by your class
that has not been compiled since last change.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 147
Running a Class
◆ Run a class? What does that mean? It means that if your
class contains a method with this header:
◆ then you can "run" the main method with the java command:
07/27/08 Copyright
PL/SQL Advanced Techniques - page 149
Compare Performance of Methods
◆ Use System.currentTimeMillis to calculate elapsed time to
nearest thousandth of a second.
class Tmr {
private long Gstart = 0; p.java
Tmr.java
public void capture () { InFile.java
Gstart = System.currentTimeMillis(); }
◆ Abstract class
– The class can contain members and methods, but at least one
method remains unimplemented.
◆ Interface class
– The class only contains unimplemented methods.
◆ Inner class
– Classes that are defined inside other classes.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 151
And Now for Some Scary Buzzwords!
◆ Inheritance
– Subclasses inherit methods and variables from extended superclasses.
◆ Polymorphism
– An object of a class can have "multiple" forms, either as its own class or as
any superclass.
– Dynamic polymorphism: form is determined at run-time.
– Static polymorphism: form is chosen at compile-time (PL/SQL overloading).
supertype/"wider" subtype/"narrower"
Person
Employee Hourly Worker
Management
A Class
Salaried
Hierarchy Corporation Worker
Non-
Management
07/27/08 Copyright
PL/SQL Advanced Techniques - page 152
Language Basics
◆ Comments // Single line comment
/* Block
comment */
07/27/08 Copyright
PL/SQL Advanced Techniques - page 153
Writing Loops in Java
◆ Three kinds of loops: while, do-while and for
– Note: you need squiggly brackets only if there is more than one
statement in the loop body.
class PuterLingo {
private String mname;
public PuterLingo (String name) { mname = name; }
public String name () { return mname; }
}
class LotsALingos {
public static void main (String args[]) {
PuterLingo Java = new PuterLingo("Java");
System.out.println (Java.name()); }
}
07/27/08 Copyright
PL/SQL Advanced Techniques - page 155
Exception Handling in Java
◆ Very similar to PL/SQL; you "throw" and "catch" exceptions,
rather than raise and handle.
Throwing my own
public static int numBytes (String filename) { exception
try {
if (filename.equals(""))
throw new Exception ("Filename is NULL");
Put inside
try clause File myFile = new File (filename);
return myFile.length();
} File-related
catch (SecurityException e) { exceptions
return -1;
}
catch (Exception e) {
System.out.println (e.toString()); "WHEN OTHERS"
} in Java
}
07/27/08 Copyright
PL/SQL Advanced Techniques - page 156
Differences Between Java & PL/SQL
◆ Exceptions are objects derived directly or indirectly from the
Exception class.
– So you can pass them as arguments and do anything and everything
else you can do with objects.
◆ You must inform users of your method of which exceptions
may be thrown.
– Use the throws clause in your specification, as in:
07/27/08 Copyright
PL/SQL Advanced Techniques - page 157
Java's Not So Tough!
◆ You can learn enough Java in less than a week to:
– Build simple classes
– Leverage Java inside PL/SQL
◆ Now let's explore how you can put Java to work for you
inside PL/SQL programs.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 158
Java Stored Procedures (JSPs)
Java applet or
app. using
JDBC or
SQLJ Oracle 8i server
07/27/08 Copyright
PL/SQL Advanced Techniques - page 161
Create Java class(es)
class Corporation extends Person {
long layoffs;
long CEOCompensation;
Notes on Java
public Corporation (
String Pname, long Playoffs, long PceoComp) {
classes
name = Pname;
layoffs = Playoffs; ◆ toString method
CEOCompensation = PceoComp;
}
automatically used by
System.out.println
public String toString () {
return name + ◆ main method is used
" is a transnational entity with " + layoffs +
" laid-off employees, paying its" +
to test the class.
" Chief Executive Officer " + CEOCompensation;
}
◆ Entry points must be
public static in most
public static void main (String[] args) {
// A very scary company
cases
Corporation TheGlobalMonster =
new Corporation (
◆ Classes may call other
"Northrup-Ford-Mattel-Yahoo-ATT", classes
5000000, 50000000);
◆ Avoid GUI calls
System.out.println (TheGlobalMonster);
}}
07/27/08 Copyright person.java
PL/SQL Advanced Techniques - page 162
Upload using “loadjava” utility
Java Oracle 8i server
.class file resource
file Java Java
loadjava class resource
.java file .jar file
Java
source
BEGIN
DBMS_OUTPUT.PUT_LINE(hello_emp(7499));
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 165
Publishing -- more concepts
◆Shape mapping
– Java methods declared “void” become PL/SQL
procedures
– Signature mismatches detected only at runtime
◆Type mapping (typical)
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
07/27/08 Copyright
PL/SQL Advanced Techniques - page 166
Publish in PL/SQL Package Spec
◆ Designate Java module in PL/SQL package spec...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 167
Publish as module in package body
◆ ...or in package body
CREATE OR REPLACE PACKAGE hello_pkg2
AS
FUNCTION hi_emp (empno_in IN NUMBER)
RETURN VARCHAR2;
END;
/
07/27/08 Copyright
PL/SQL Advanced Techniques - page 170
Passing Oracle8 objects
Object Java
in O-R object
table
Generated Java
file(s) for use in
Java programs
07/27/08 Copyright
PL/SQL Advanced Techniques - page 172
Passing object using JPub-generated Java
◆ After generating and uploading classes with JPub,
they become available to use in mapping
◆ Example of passing an Account_t object:
jspobj.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 173
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.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 174
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).
07/27/08 Copyright
PL/SQL Advanced Techniques - page 175
My Own Java Class for File Manipulation
◆ Accept the name of a file and return its length.
import java.io.File;
public class JFile2 {
public static long length (String fileName) {
File myFile = new File (fileName);
return myFile.length(); }
} JFile2.java
07/27/08 Copyright
PL/SQL Advanced Techniques - page 176
Build a 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.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 177
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.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 178
Translate Boolean to Number
◆ Accept the name of a file and return its length.
import java.io.File;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 179
Wrapper for 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';
System.out.println (
"Pay " + laborType + " $" + Extract individual
hourly_rate + " per hour"); attribute values from
} the array.
UnionBuster.java
}
passobj.tst
07/27/08 Copyright
PL/SQL Advanced Techniques - page 181
Wrapping a STRUCT-based Method
◆ You can pass Oracle object information to Java
without relying on JPub by using the STRUCT class.
CREATE TYPE labor_source_t AS OBJECT (
labor_type VARCHAR2(30), The Oracle object
hourly_rate NUMBER); type definition
/
BEGIN
bust_em_with ( Fully specify the
labor_source ('Workfare', 0)); STRUCT class in
bust_em_with ( parameter list.
labor_source ('Prisoners', '5'));
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 182
Viewing Output from Java Methods
◆ Java provides a "print line" method similar to
DBMS_OUTPUT.PUT_LINE: System.out.println.
– Call it within methods and output will display in your Java
environment.
◆ Any error not caught by the JVM (Java virtual machine) will
be thrown back to the PL/SQL block or SQL statement.
– And also spew out the entire Java error stack! (at least through
8.1.5).
07/27/08 Copyright
PL/SQL Advanced Techniques - page 184
Trapping and Identifying Errors
◆ Currently, the entire Java stack is displayed on your screen,
and you have to do some digging to extract the Oracle error
information.
SQL> BEGIN
2 dropany ('TABLE', 'blip');
3 EXCEPTION
4 WHEN OTHERS
5 THEN
6 DBMS_OUTPUT.PUT_LINE (SQLCODE);
7 DBMS_OUTPUT.PUT_LINE (SQLERRM);
8 END;
9 /
java.sql.SQLException: ORA-00942: table or view does not exist
at oracle.jdbc.kprb.KprbDBAccess.check_error(KprbDBAccess.java)
at oracle.jdbc.kprb.KprbDBAccess.parseExecuteFetch(KprbDBAccess.java)
at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java)
at oracle.jdbc.driver.OracleStatement.doExecuteWithBatch(OracleStatement.java)
at oracle.jdbc.driver.OracleStatement.doExecute(OracleStatement.java)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java)
at oracle.jdbc.driver.OracleStatement.executeUpdate(OracleStatement.java)
at DropAny.object(DropAny.java:14) DropAny.java
-29532 dropany.tst
ORA-29532: Java call terminated by uncaught Java exception: java.sql.SQLException:
getErrInfo.sp
ORA-00942: table or view does not exist dropany2.tst
07/27/08 Copyright
PL/SQL Advanced Techniques - page 185
Achieving PL/SQL Excellence
External Procedures
◆ An external procedure is a 3GL routine that can serve as
the “body” of a PL/SQL function, procedure, or method.
– Must be a shared object library on Unix or a dynamically linked
library (DLL) on Windows.
◆ An Oracle8 feature that allowed relatively "native"
callouts to C from PL/SQL for the first time.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 186
Process & data flow
Application Net8 External
Shared
Makes Library
External
request
Procedur
PL/SQL e Listener
Runtime calls
Client or routine
Routine in
Calls
server- Engine spawns
from .DLL or
side PL/SQL .so file
applicatio body
n returns extproc
results returns
returns results
results
07/27/08 Copyright
PL/SQL Advanced Techniques - page 187
External procedures: Sample uses
◆ Send email email.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 188
Determine free disk space on NT (1/4)
◆ Use pre-existing routine:
– GetDiskFreeSpaceA in kernel32.dll
– For given drive letter, this function returns:
» sectors per cluster
» bytes per sector
» number of free clusters
» total number of clusters
» “return code” indicating success or failure
07/27/08 Copyright
PL/SQL Advanced Techniques - page 189
Package spec (2/4)
◆ Nothing unexpected here:
END disk_util;
/
07/27/08 Copyright
PL/SQL Advanced Techniques - page 190
Package body in Oracle 8.0 (3/4)
CREATE OR REPLACE PACKAGE BODY disk_util AS
FUNCTION get_disk_free_space
(root_path IN VARCHAR2,
sectors_per_cluster OUT PLS_INTEGER,
bytes_per_sector OUT PLS_INTEGER,
number_of_free_clusters OUT pls_integer,
total_number_of_clusters OUT PLS_INTEGER)
RETURN PLS_INTEGER
IS EXTERNAL
LIBRARY nt_kernel
NAME "GetDiskFreeSpaceA"
◆ All the
LANGUAGE C
magic is in CALLING STANDARD PASCAL
the PARAMETERS
EXTERNAL (root_path STRING,
section sectors_per_cluster BY REFERENCE LONG,
bytes_per_sector BY REFERENCE LONG,
number_of_free_clusters BY REFERENCE LONG,
total_number_of_clusters BY REFERENCE LONG,
RETURN LONG);
07/27/08 Copyright
END disk_util;
PL/SQL Advanced Techniques - page 191
Usage (4/4)
DECLARE
lroot_path VARCHAR2(3) := 'C:\';
lsectors_per_cluster PLS_INTEGER;
lbytes_per_sector PLS_INTEGER;
lnumber_of_free_clusters PLS_INTEGER;
ltotal_number_of_clusters PLS_INTEGER;
return_code PLS_INTEGER;
free_meg REAL;
BEGIN
return_code := disk_util.get_disk_free_space (lroot_path,
lsectors_per_cluster, lbytes_per_sector,
lnumber_of_free_clusters, ltotal_number_of_clusters);
07/27/08 Copyright
PL/SQL Advanced Techniques - page 193
1. Identify/create external routine
◆ Prerequisite: O/S must support shared libraries
07/27/08 Copyright
PL/SQL Advanced Techniques - page 194
2. Create the Oracle library
◆ Syntax:
CREATE OR REPLACE LIBRARY <library name>
AS
'<full path to file>';
07/27/08 Copyright
PL/SQL Advanced Techniques - page 195
3. Map the parameters
07/27/08 Copyright
PL/SQL Advanced Techniques - page 196
Achieving PL/SQL Excellence
Autonomous Transactions
Invoker Rights Model
Row Level Security
07/27/08 Copyright
PL/SQL Advanced Techniques - page 197
Oracle8i New Features
◆ Oracle8i offers a number of PL/SQL-specific features that
give you tremendous additional flexibility and capability.
– And the learning curve to take advantage of these features is
generally not too steep.
◆ Autonomous Transactions
◆ The Invoker Rights Model
◆ Row level security
07/27/08 Copyright
PL/SQL Advanced Techniques - page 198
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.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 199
When to Use Autonomous Transactions
◆ Reusable Application Components
– ATs are more or less required in the new distributed application
architecture of the Internet.
– One component should not have any impact (esp. something like a
COMMIT) on other components.
◆ Logging Mechanism
– Commonly developers lot to database tables, which can cause all
sorts of complications: your log entry becomes a part of your
transaction.
– Now you can avoid the complexities (need for ROLLBACK TO
savepoints and so on).
◆ Tracing Code Usage
– Build retry mechanisms, software usage meter, etc.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 201
Tips and Gotchas with ATs
◆ An AT program must COMMIT or ROLLBACK before terminating, or an
error is raised.
◆ 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 to be 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.
◆ 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
auton_in_sql.sql
07/27/08 Copyright autontrigger*.sql
PL/SQL Advanced Techniques - page 202
The Invoker Rights Model
◆ 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 …
07/27/08 Copyright
PL/SQL Advanced Techniques - page 203
About Definer Rights
◆ Allows you to centralize
OE Code
access to and control of Sam_Sales
Order_Mgt
underlying data Place
Close Old
structures. Cancel Orders
07/27/08 Copyright
PL/SQL Advanced Techniques - page 204
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.
– Can bypass 8i’s “fine-grained” access features (see the DBMS_RLS
package)
– Difficult to audit privileges
07/27/08 Copyright
PL/SQL Advanced Techniques - page 205
Oracle8i Invoker Rights Syntax
◆ For top level modules:
07/27/08 Copyright
PL/SQL Advanced Techniques - page 206
"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.
...FROM accounts
accounts table
WHERE...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 207
Tips and Gotchas for Invoker Rights
◆ Does not apply to code objects, only data
– When your stored code references another stored code element, the
definer rights model is always applied to resolve the reference.
– Both static and dynamic SQL is supported.
◆ Once a definer rights program is called, all other calls in
stack are resolved according to definer rights.
– AUTHID CURRENT_USER is ignored.
◆ Information about the rights model is not available in the
data dictionary
◆ What if you want to maintain a single version of your code
for both pre-Oracle8i and Oracle8i installations, taking
advantage of the invoker rights model whenever possible?
– A creative use of SQL*Plus substitution variables comes in very
handy. Note: cannot use with wrapped code. invdefinv.sql
oneversion.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 208
When to Invoke Invoker Rights
◆ You want to reuse the same code among many users, but
you want their own directly-granted privileges to determine
access.
National HQ
New York
Schema
Chicago Check City
Schema Statistics
stolenlives stolenlives
07/27/08 Copyright
PL/SQL Advanced Techniques - page 209
Combining Invoker & Definer Rights
◆ Rely on invoker rights to allow centralized code to work with
schema-specific data.
◆ Rely on definer rights to access centralized data from any
schema.
HQ
Chicago Check City New York
Statistics
Analyze
stolenlives Pattern stolenlives
perpetrators
perp.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 210
Row Level Security
DBMS_RLS
Row-Level
Security
07/27/08 Copyright
PL/SQL Advanced Techniques - page 211
Row-Level Security with DBMS_RLS
◆ Oracle8i offers a new package, DBMS_RLS, with which to
implement automated row-level security (also referred to as
"fine grained access control").
07/27/08 Copyright
PL/SQL Advanced Techniques - page 212
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
– You will need to 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.
◆ Let's step through a simple example to see how all these pieces tie
together.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 213
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. The
main tables are patient, doctor, clinic and regulator.
◆ Here are some rules:
– 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.
fgac.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 214
Set the Context
◆ Define a context in the database, and create a procedure
that will set the context attributes (type and ID) upon login.
PROCEDURE set_context
IS This is a simplification. See
CURSOR doc_cur IS fgac.sql for logic that identifies
SELECT doctor_id FROM doctor different types of people and
WHERE schema_name = USER; sets the context accordingly.
doc_rec doc_cur%ROWTYPE;
BEGIN
OPEN doc_cur; FETCH doc_cur INTO doc_rec;
DBMS_SESSION.SET_CONTEXT (
'patient_restriction', c_person_type_attr, 'DOCTOR');
DBMS_SESSION.SET_CONTEXT (
'patient_restriction', c_person_id_attr, doc_rec.doctor_id);
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 215
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
retval := 'home_clinic_id IN
(SELECT home_clinic_id FROM doctor We need a different string
WHERE doctor_id = SYS_CONTEXT (''' || to modify the WHERE
c_context || ''', ''' || clause for each type of
c_person_id_attr || '''))'; person.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 216
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');
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 217
Create Logon Trigger, Setting Context
◆ By setting the context in the logon trigger, we guarantee that
the context is set (and the predicate applied) no matter
which product is the entry point to Oracle.
fgac.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 218
Queries Transparently Filtered
◆ What you see is determined automatically by who you are.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 219
Take Full Advantage of PL/SQL!
January... February... March... April... May...
07/27/08 Copyright
PL/SQL Advanced Techniques - page 220
Achieving PL/SQL Excellence
Appendices
(a.k.a., If time permits…)
◆ UTL_FILE – file IO
◆ DBMS_JOB – Job scheduling
◆ DBMS_PIPE – Pipe-based communication
◆ DBMS_UTILITY – the kitchen sink package
07/27/08 Copyright
PL/SQL Advanced Techniques - page 221
Built-in Packages: UTL_FILE
UTL_FILE
Server-side
File I/O
Application
FOPEN
GET_LINE
PUT_LINE
...
Physical Files
07/27/08 Copyright
PL/SQL Advanced Techniques - page 222
UTL_FILE: Server-side I/O
◆ Allows you to read from and write to operating system
files on the database server.
◆ But you can read lines from a file and write lines to a file.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 223
UTL_FILE Module Outline
FCLOSE Close the specified file
FFLUSH Flush all data from the UTL_FILE buffer to your file
NEW_LINE Insert a new line character in file at the end of current line
PUT_LINE Puts text and new line character into UTL_FILE buffer.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 227
Reading from a File
DECLARE
fid UTL_FILE.FILE_TYPE;
BEGIN
fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'R');
UTL_FILE.GET_LINE (fid, myline);
UTL_FILE.FCLOSE (fid);
END;
◆ Can only read from a file opened with the "R" mode.
◆ Lines in the file cannot be longer than 1023 bytes in length.
– In Oracle8 Release 8.0.5 and above, the ceiling is raised to 32K.
◆ If you do not close the file, you will not see the data you have
(supposedly) written to that file.
◆ You can close a single file with FCLOSE or all open files with
FCLOSE_ALL.
◆ You should close files in exception handlers to make sure that files are
not left "hanging" open.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 230
Error Handling in UTL_FILE
PACKAGE UTL_FILE
IS
invalid_path EXCEPTION;
invalid_mode EXCEPTION;
invalid_filehandle EXCEPTION;
invalid_operation EXCEPTION;
read_error EXCEPTION;
write_error EXCEPTION;
internal_error EXCEPTION;
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 231
Recommended Exception Section
EXCEPTION
WHEN UTL_FILE.INVALID_PATH
THEN recNgo (PLVfile.c_invalid_path); RAISE;
WHEN UTL_FILE.INVALID_MODE
THEN recNgo (PLVfile.c_invalid_mode); RAISE;
WHEN UTL_FILE.INVALID_FILEHANDLE
THEN recNgo (PLVfile.c_invalid_filehandle); RAISE;
WHEN UTL_FILE.INVALID_OPERATION
THEN recNgo (PLVfile.c_invalid_operation); RAISE;
WHEN UTL_FILE.READ_ERROR
THEN recNgo (PLVfile.c_read_error); RAISE;
WHEN UTL_FILE.WRITE_ERROR
THEN recNgo (PLVfile.c_write_error); RAISE;
utlflexc.sql
WHEN UTL_FILE.INTERNAL_ERROR
THEN recNgo (PLVfile.c_internal_error); RAISE;
END;
◆ Trap locally by name; record the error, translating the generic
user-defined exception into an understandable message.
◆ Re-raise exception if you want it to propagate from that block.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 232
Built-in Packages: DBMS_JOB
DBMS_JOB
Scheduled Execution
of Stored Procedures
Oracle Job Queue
Application Subsystem
SUBMIT
DBA_JOBS
RUN
REMOVE
...
Background
Process
07/27/08 Copyright
PL/SQL Advanced Techniques - page 233
Overview of DBMS_JOB
◆ DBMS_JOB provides an API to the Oracle job queues, which
in turn offers job scheduling capabilities within the Oracle
Server.
◆ Built by Oracle to support snapshots and replication.
◆ Made its debut in PL/SQL Release 2.1, but only “publicly
available” and supported in PL/SQL Release 2.2.
◆ You can use DBMS_JOB to:
– Replicate data between different database instances.
– Schedule regular maintenance on instances.
– Schedule large batch jobs to run on "off hours".
– Create a listener program to poll the contents of a pipe and take
action.
– Spawn background processes to avoid blocking client process.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 234
DBMS_JOB Module Outline
BROKEN Marks the job as either FIXED or BROKEN.
Broken jobs will not run as scheduled.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 235
Submitting a Job
DECLARE
job# BINARY_INTEGER;
BEGIN
DBMS_JOB.SUBMIT
(job#, 'calculate_totals;', SYSDATE, 'SYSDATE + 1');
END;
◆ This block submits three jobs to the job queue, numbered 1,2,
and 3.
– Job 1 passes a string and number into procedure MY_JOB1, runs it in one
hour and executes every day thereafter.
– Job 2 passes a date into procedure MY_JOB2, executes for the first time
tomorrow and every 10 minutes thereafter.
– Job 3 is a PL/SQL block which does nothing, executes immediately, and
will be removed from the queue automatically.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 238
Specifying Job Times & Frequencies
◆ Probably the most complicated part of using DBMS_JOB is to
get the string expression of the job interval right.
– Since it's a string, you must use 2 single quotes to embed strings.
– Use date arithmetic to request intervals smaller than a day.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 239
Other Job Queue Operations
◆ Remove a job from the queue.
– Only if the job was submitted from same schema
Remove all jobs
for current
BEGIN schema.
FOR rec IN (SELECT * FROM USER_JOBS) LOOP
DBMS_JOB.REMOVE (rec.job);
END LOOP;
FROM DBA_JOBS_RUNNING jr
,DBA_JOBS j
,V$SESSION s
07/27/08 Copyright
PL/SQL Advanced Techniques - page 241
Setting up the Job Facility
◆ Make sure that the correct access is set up for the
DBMS_JOB package.
– The default is PUBLIC access. You will have to take special DBA
action if you want to restrict who can run jobs.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 242
Error Handling with DBMS_JOB
◆ What if your stored procedure fails?
– After 16 attempts, the job facility will mark your job as broken.
– Do you want it to try 16 times?
– In addition, if your failure raises an unhandled exception, it may
cause the background processes to fail and no longer run any
jobs at all.
◆ To avoid unexpected and unhandled failures of scheduled
jobs:
– Always use the RUN built-in to execute your job in a “test” mode.
Then you can go ahead and submit it.
– Always include a WHEN OTHERS exception handler in your job
program which traps any and all problems and automatically sets
the job status to broken.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 243
Stopping the Job When It Fails
PROCEDURE calc_totals IS
BEGIN
...
EXCEPTION
WHEN OTHERS
THEN
job# := job_pkg.job (‘calc_totals’)
spacelog.sql
DBMS_JOB.BROKEN (job#, TRUE); showspc.sql
job_pkg.log (‘calc_totals’, ‘FAIL’);
END;
◆ The WHEN OTHERS exception handler of the calc_totals
procedure traps any kind of failure.
– Obtains the job number from a packaged function by passing the
name of the procedure.
– Uses a call to BROKEN to set the status of the job to “broken”.
– Calls log program of package to record that failure took place.
– Now the job facility will not try to run this program again. You can go
in and fix the problem.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 244
"Good to Knows" for DBMS_JOB
◆ You may find it useful to always wrap your stored procedure call (the
what) inside a BEGIN-END block.
– We've noticed some aberrant, difficult to reproduce behavior at times with
"straight" procedure calls.
◆ You will need to fully-qualify all database links (with user name and
password) to get them to work properly.
◆ If you find that you submit a job to run immediately and it does not start,
perform a COMMIT after your submit.
◆ When a job runs, it picks up the current execution environment for the
user.
◆ You can use the DBMS_IJOB to manage the jobs of other users.
– DBMS_JOB only allows you to modify characteristics and behavior of jobs
submitted by the current schema.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 245
Built-in Packages: DBMS_PIPE
DBMS_PIPE
Inter-session
Communication
07/27/08 Copyright
PL/SQL Advanced Techniques - page 246
DBMS_PIPE Overview
◆ Allows communication between different Oracle sessions
through a pipe in the RDBMS Shared Global Area.
– Operates outside of database transaction limitations.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 247
Architecture of DBMS_PIPE
Shared Global Area
“Sue”
“Bob”
Session A Session B
PACK_MESSAGE Packs an item into the message buffer for your session.
PURGE Empties the contents of a pipe into your local buffer freeing it for
removal, making it a candidate for removal with a LRU algorithm.
UNIQUE_SESSION_NAME Returns name that is unique among all sessions in the database.
UNPACK_MESSAGE
Unpacks the next item from the local message buffer and deposits it into
the specified local variable.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 250
Creating Public and Private Pipes
◆ There are two ways to create pipes: implicitly and
explicitly.
– Send messages to non-existent pipe implicitly create it.
– Use CREATE_PIPE to create a pipe explicitly.
PROCEDURE newpipe IS
stat INTEGER;
BEGIN
stat := DBMS_PIPE.CREATE_PIPE (
pipename => 'bbs', maxpipesize => 20000, private => TRUE);
FOR month_num IN 1 .. 12
Fill your message buffer
LOOP
with packets of data.
DBMS_PIPE.PACK_MESSAGE (
total_sales (month_num));
END LOOP;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 252
Receiving and Unpack a Message
FUNCTION receive_message
(pipename IN VARCHAR2, timeout IN INTEGER DEFAULT maxwait)
RETURN INTEGER;
◆ First you pull the message from the pipe and place it in buffer
with RECEIVE_MESSAGE.
– Specify pipe and number of seconds you will wait before you time out.
– Pipe status of 0 means the message was read successfully.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 253
A Pipe-based Listener Program
◆ Program wakes up every N seconds to read the data from the pipe and
analyze results from assembly line (an intentional "infinite loop"!).
07/27/08 Copyright
PL/SQL Advanced Techniques - page 256
Sequential vs. Parallel Execution
Sequential Execution: Maximum Elapsed Time
S
t E
a n
r Process A Process B Process C d
t
Start
End
07/27/08 Copyright
PL/SQL Advanced Techniques - page 257
Parallelizing PL/SQL Execution
BEGIN
kick_off_sales_calc;
kick_off_exp_calc;
kick_off_totcomp_calc;
wait_for_confirmation;
calculate_net_profits;
END;
◆ The “kick off” programs pass their messages to separate
pipes.
– Other programs running in the background grab the messages and start
their corresponding calculation program.
– When each program is complete, it puts a message (perhaps even a
value) into the pipe.
◆ The “wait” program waits till it receives a message from each
program. Then net profits can be computed -- in a much-
decreased elapsed time.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 258
Code to Kick Off and Calculate
PROCEDURE kick_off_sales_calc IS
stat INTEGER; Send message to start
BEGIN calculations.
DBMS_PIPE.PACK_MESSAGE (1995);
stat := DBMS_PIPE.SEND_MESSAGE (‘sales’);
END;
PROCEDURE calculate_sales IS
stat INTEGER;
BEGIN
stat := DBMS_PIPE.RECEIVE_MESSAGE (‘sales’);
IF stat = 0
THEN
lots_of_number_crunching; Receive the year,
calculate sales, and send
DBMS_PIPE.PACK_MESSAGE (sales$); back the results.
stat := DBMS_PIPE.SEND_MESSAGE (‘sales’);
ELSE
DBMS_PIPE.PACK_MESSAGE (NULL);
stat := DBMS_PIPE.SEND_MESSAGE (‘sales’);
END IF;
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 259
Waiting for Confirmation
PROCEDURE wait_for_confirmation
IS
stat INTEGER;
BEGIN
stat := DBMS_PIPE.RECEIVE_MESSAGE (‘sales’); Wait for all calculations to
DBMS_PIPE.UNPACK_MESSAGE (sales$); finish.
PROCEDURE calculate_net_profits IS
BEGIN Perform final
net_profits := sales$ - offexp$ - comp$; calculation.
END;
parallel.sql
07/27/08 Copyright
PL/SQL Advanced Techniques - page 260
Other DBMS_PIPE Examples
◆ Simple trace package that sends it output either to screen or to pipe.
– Demonstrates the use of toggles and switches in packages.
watch.pkg
p_and_l.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 261
Built-in Packages: DBMS_UTILITY
DBMS_UTILITY
Application
GET_TIME
GET_HASH_VALUE
FORMAT_CALL_STACK
...
A ‘grab-bag’
of miscellaneous SCHEMA
operations
07/27/08 Copyright
PL/SQL Advanced Techniques - page 262
DBMS_UTILITY Module Outline
ANALYZE_DATABASE Analyze objects in database
MAKE_DATA_BLOCK_ADDRESS Creates data block address from block & file numbers.
07/27/08 Copyright
PL/SQL Advanced Techniques - page 264
Sub-Second Timings with GET_TIME
◆ GET_TIME returns the number of 100ths of seconds that
have elapsed since an arbitrary point in time.
– SYSDATE only reflects times down to the nearest second, so
GET_TIME offers significant additional granularity.
– Useful when analyzing individual PL/SQL programs, especially
those that run in sub-second time.
◆ Compare results from consecutive calls to GET_TIME to
determine the elapsed time of PL/SQL code execution.
DECLARE
v_start BINARY_INTEGER;
BEGIN
Basic steps necessary
v_start := DBMS_UTILITY.GET_TIME;
to convert GET_TIME
calc_totals;
into a performance
analysis tool.
DBMS_OUTPUT.PUT_LINE
(DBMS_UTILITY.GET_TIME - v_start);
END;
07/27/08 Copyright
PL/SQL Advanced Techniques - page 265
Building a Layer Over GET_TIME
PACKAGE PLVtmr
IS
Partial package PROCEDURE turn_on;
specification PROCEDURE turn_off;
PROCEDURE set_factor (factor_in IN NUMBER);
PROCEDURE capture (
context_in IN VARCHAR2 := NULL)
PROCEDURE show_elapsed
(prefix_in IN VARCHAR2 := NULL,
adjust_in IN NUMBER := 0,
reset_in IN BOOLEAN := TRUE);
END PLVtmr;
BEGIN
PLVtmr.capture;
Clean and lean timing code calc_totals;
PLVtmr.show_elapsed;
ovrhead.sql END;
plvtmr.sps
plvtmr.spb
07/27/08 Copyright
PL/SQL Advanced Techniques - page 266
Accessing the Execution Call Stack
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (
Use in the
DBMS_UTILITY.FORMAT_CALL_STACK);
exception
END;
section to show
context of ----- PL/SQL Call Stack -----
error. object line object
handle number name
07/27/08 Copyright
PL/SQL Advanced Techniques - page 267
Accessing Call Stack Contents
◆ The call stack length can easily exceed 255 bytes, which means you
cannot pass it to DBMS_OUTPUT directly. Instead, use a loop to read
through the stack.
CREATE OR REPLACE PROCEDURE dispcs IS
stk VARCHAR2(10000);
next_newline INTEGER;
next_line VARCHAR2(255);
startpos INTEGER := 1;
BEGIN
stk := DBMS_UTILITY.FORMAT_CALL_STACK || CHR(10);
LOOP
next_newline := INSTR (stk, CHR(10), startpos, 1);
EXIT WHEN next_newline = 0;
next_line := SUBSTR (
stk, startpos, next_newline - startpos + 1);
showcomp.sp
snc.pkg
07/27/08 Copyright
PL/SQL Advanced Techniques - page 269