Oracle Advanced Techniques PL SQL
Oracle Advanced Techniques PL SQL
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
12/08/21 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
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 3
Achieving PL/SQL Excellence
Building with
PL/SQL Packages
Overview
Initialization section
Overloading
12/08/21 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
12/08/21 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.
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 7
Package Initialization Structure
The initialization section:
– Is defined after and outside of any programs
PACKAGE BODY pkg
in the package. IS
– Is not required. In fact, most packages you PROCEDURE proc IS
build won't have one. BEGIN
END;
– Can have its own exception handling
section. FUNCTION func RETURN
BEGIN
Useful for: END;
– Performing complex setting of default or BEGIN
initial values. END pkg;
– Setting up package data which does not
change for the duration of a session. BEGIN after/outside
– Confirming that package is properly of any program
instantiated. defined in the pkg.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 8
Configure Session with Init. Section
PACKAGE BODY sessinit IS
An unusual package! /* No declared package elements at all! */
– Specification contains BEGIN
only variables. /* Get user preferences for this user. */
SELECT lov_flag, tb_flag, defprinter
– Body contains only INTO show_lov, show_toolbar, printer
initialization section. FROM user_config
WHERE user_id = USER;
PACKAGE sessinit
IS EXCEPTION
show_lov CHAR(1); WHEN NO_DATA_FOUND
show_toolbar CHAR(1); THEN
printer VARCHAR2(60); /* No record for this user. */
END sessinit; show_lov := 'Y';
show_toolbar := 'Y';
printer := 'lpt1';
Also a package with
WHEN OTHERS
many design flaws... THEN
RAISE_APPLICATION_ERROR
init.pkg (-20000,
init.tst 'No profile for ' || USER);
END sessinit;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 9
Populate Collections
The PL/Vision Date package, PLVdate, employs several
PL/SQL tables to convert strings and to perform date
arithmetic.
– This increases the flexibility of the date conversion process.
– The datemgr.pkg file demonstrates the basic technique (and the
reliance on an initialization section) used to achieve this flexibility.
BEGIN
fmts(1) := 'DD-MON-RR';
fmts(2) := 'DD-MON-YYYY';
fmts(3) := 'DD-MON'; Initialization section
fmts(4) := 'MM/DD'; populates a
... PL/SQL table.
fmts(9) := 'MM/DD/YYYY';
fmts(10) := 'MMDDYYYY';
fmts(11) := 'YYYYMMDD';
fmts(12) := 'RRMMDD';
datemgr.pkg
fmt_count := 12;
dates.sql
END dt;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 10
Program Overloading
When you overload programs, you give
two or more programs the same name.
– You can overload modules in any declaration
section and in packages.
myproc
number_string :=
TO_CHAR_FROM_NUMBER (10000);
12/08/21 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.
12/08/21 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.
12/08/21 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.
Overloading by Type, not Value
– A less common application of overloading. You use the type of data and not
its value to determine which of the overloaded programs should be executed.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 15
A "Classic" Overloaded Package
Many different datatype combinations, allowing the user to pass data to the "display
engine" without writing "pre-processing" code.
PACKAGE p
IS
PROCEDURE l
(date_in IN DATE,
mask_in IN VARCHAR2 := ‘Month DD, YYYY - HH:MI:SS PM');
PROCEDURE l
(char_in IN VARCHAR2, date_in IN DATE,
mask_in IN VARCHAR2 := 'Month DD, YYYY - HH:MI:SS PM');
END IF;
12/08/21 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.
12/08/21 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!
12/08/21 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.
12/08/21 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, 1);
DBMS_SQL.DEFINE_COLUMN (cur, 2, 'a', 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);
12/08/21 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);
12/08/21 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);
12/08/21 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;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 26
Using Named Notation
<formal parameter name> => <expression>
Explicit association between the formal parameter (the "name")
with the actual parameter (the "value").
Advantages of named notation include:
– Code is more "self-documenting". This is especially useful when working
with infrequently used built-in programs.
– You can skip over (not specify values for) any IN parameters that have
default values. That way you don't have to know and pass default values.
DBMS_JOB.submit ( namednot.sql
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)');
12/08/21 Copyright
PL/SQL Advanced Techniques - page 27
Achieving PL/SQL Excellence
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)
12/08/21 Copyright
PL/SQL Advanced Techniques - page 28
When to Use Collections
Maintain any kind of list of related information for use in your
programs.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 29
Index-By Tables
TYPE <table_type> IS TABLE OF <datatype>
INDEX BY BINARY_INTEGER;
DECLARE
TYPE inmem_emp_t IS TABLE OF emp%ROWTYPE
INDEX BY BINARY_INTEGER;
emp_copy inmem_emp_t;
Characteristics of an index-by table:
– Unbounded
» Practically speaking. Valid row numbers range: -2,147,483,647 to 2,147,483,647
» You will not actually create tables this large. Instead, this broad range allows you to employ the row number
as an intelligent key.
– Sparse
» Data does not have to be stored in consecutive rows of information.
– Homogeneous
» Data in each row has the same structure.
– Available only in PL/SQL
12/08/21 Copyright
PL/SQL Advanced Techniques - page 30
Index_by Tables
TYPE PACKAGE BODY family IS
declaration
TYPE child_list_type IS
TABLE OF VARCHAR2 (30)
INDEX BY BINARY_INTEGER;
Variable
declaration children child_list_type;
Error: datemgr.pkg
NO_DATA_FOUND
12/08/21 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;
12/08/21 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
12/08/21 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;
Characteristics of variable arrays:
– Homogeneous
» Each row contains the same structure of data.
– Bounded
» Upper limit established when the TYPE is defined. Maximum value: 2,147,483,647
– Dense
» Never any gaps between defined rows, can be EXTENDed or TRIMmed.
– Available both in PL/SQL and SQL (as a column in a table)
» And the order of elements are preserved in the database.
– Variable arrays are actually stored in the DB table
12/08/21 Copyright
PL/SQL Advanced Techniques - page 34
Variable Arrays
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
12/08/21 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;
12/08/21 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
There is no information in the data dictionary available for index-by tables.
colldd.sql
12/08/21 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
12/08/21 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);
12/08/21 Copyright
PL/SQL Advanced Techniques - page 40
Transferring DB Table to Collection
CREATE OR REPLACE PACKAGE psemp
IS
This package moves the entire
TYPE emp_tabtype IS contents of the emp table into
TABLE OF emp%ROWTYPE its corresponding collection.
INDEX BY BINARY_INTEGER;
Some questions:
emp_tab emp_tabtype;
– Why would I put this collection
END;
table in a package?
CREATE OR REPLACE PACKAGE BODY psemp – When is the collection loaded
IS with the data?
BEGIN
FOR rec IN (SELECT * FROM emp) – What rows in that collection
LOOP are utilized?
emp_tab (rec.empno) := rec;
END LOOP;
END;
Initialization psemp.pkg
section of psemp.tst
package
12/08/21 Copyright
PL/SQL Advanced Techniques - page 41
Collection Gotchas
CREATE TYPE names_t IS TABLE OF VARCHAR2(30);
/
DECLARE Error -6533!
greedy_ceos names_t := names_t ();
BEGIN You've got to
greedy_ceos(1) := 'Hamilton, Jordan'; EXTEND first!
END;
/
12/08/21 Copyright
PL/SQL Advanced Techniques - page 42
Handling Collection Gotchas
BEGIN
-- Extend by 10 since I know I will need that many.
-- Set the value of each new row to the contents of the first row.
salaries.EXTEND (10, salaries(salaries.FIRST));
You can EXTEND one or more rows.
– Assign a default value with a second, optional argument.
– Pre-extending a large number of rows in advance can
improve performance. preextend.tst
12/08/21 Copyright
PL/SQL Advanced Techniques - page 44
The DELETE Method
You can delete one or more rows from a collection using
DELETE:
BEGIN
-- Delete all rows
myCollection.DELETE;
12/08/21 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);
12/08/21 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
Index-by tables are programmatic constructs only.
– You cannot make a direct reference to an index-by table in SQL.
– Instead, so do indirectly with a PL/SQL function.
12/08/21 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
...
12/08/21 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.
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;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 50
Using the MULTISET Operator
MULTISET is the inverse of TABLE, converting a set of data (table, view, query) into a VARRAY or nested table.
– Cannot use with index-by tables.
– You can 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; multiset.sql
12/08/21 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;
12/08/21 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;
12/08/21 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
mutating.sql
12/08/21 Copyright
PL/SQL Advanced Techniques - page 55
A Solution Based on Index-by 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
12/08/21 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
12/08/21 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;
12/08/21 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.
12/08/21 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.
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 62
Achieving PL/SQL Excellence
Cursor Variables
12/08/21 Copyright
PL/SQL Advanced Techniques - page 63
Architecture of Cursor Variables
With Hard-Coded Cursors With Cursor Variables
Hard-Coded
Hard-Coded Cursor Cursor
Cursor Cursor
Cursor
Cursor PGA Variable
Variable Variable
Variable
Result
Shared Cursor
Result Cursor Result
Set
Set Global Object
Object
Result
Set
Set
Area
12/08/21 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;
12/08/21 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.
12/08/21 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.
12/08/21 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 ];
12/08/21 Copyright
PL/SQL Advanced Techniques - page 69
Opening with the Cursor Variable
OPEN cursor_name FOR select_statement;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 70
Opening with Strong Cursor Types
DECLARE
BEGIN
OPEN emp_curvar FOR SELECT * from emp;
...
END;
12/08/21 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
12/08/21 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.
Define a base table block in Forms Builder (formerly Oracle
Forms) on stored procedures rather than a table directly.
Use a single block of code to manipulate multiple queries.
– With explicit cursors, you have to repeat the code for each cursor,
since cursor names are "hard coded".
– You could use dynamic SQL to achieve this effect, but static cursors
are more efficient than dynamic SQL and cursor variables are less
complicated than DBMS_SQL.
hccursor.sql
12/08/21 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
12/08/21 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;
/
12/08/21 Copyright
PL/SQL Advanced Techniques - page 77
Cursor Variables
Let's
summarize
Flexibility
– Choose which static SQL statement is executed at run-time.
Strong and Weak Types
– Create REF CURSORs for specific queries, or a more general,
unconstrained type.
Hide Variations in Underlying SQL
– You no longer have to repeat the same code for different cursors.
Improve Client-Side Access to Data
– At least in Oracle Developer 2.1, you can build screens that access data
using a cursor variable-based procedural API.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 78
Achieving PL/SQL Excellence
Dynamic SQL
12/08/21 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.
12/08/21 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).
12/08/21 Copyright
PL/SQL Advanced Techniques - page 82
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE sql-string
12/08/21 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;
12/08/21 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.
str2list.pkg
12/08/21 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.
12/08/21 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
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 91
Updates with Dynamic SQL
Update numeric column for specified employees.
DBMS_SQL.CLOSE_CURSOR (cur);
updnval1.sp
END;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 92
Updates with Bind Variables
Update salaries for date range using binding.
Allocate
Allocatecursor
cursormemory
memory Fill
Fillcursor
cursorwith
withdata
data
(OPEN_CURSOR)
(OPEN_CURSOR) (EXECUTE)
(EXECUTE)
Make
Makesure
sureSELECT
SELECTisis Fill
Fillbuffer
bufferwith
withdata
data
well
wellformed
formed (FETCH_ROWS)
(FETCH_ROWS)
(PARSE)
(PARSE) (EXECUTE_AND_FETCH)
(EXECUTE_AND_FETCH)
Bind
Bindany
anyvariables
variables Retrieve
Retrievethe
thedata
data
(BIND_VARIABLE)
(BIND_VARIABLE) (COLUMN_VALUE)
(COLUMN_VALUE)
(BIND_ARRAY)
(BIND_ARRAY)
Give
Givecursor
cursorstructure
structure Release
Releasecursor
cursormemory
memory
(DEFINE_COLUMN)
(DEFINE_COLUMN) (CLOSE_CURSOR)
(CLOSE_CURSOR)
(DEFINE_ARRAY)
(DEFINE_ARRAY)
12/08/21 Copyright
PL/SQL Advanced Techniques - page 94
Queries with Dynamic SQL
Show employees using a dynamic WHERE clause...
DBMS_SQL.CLOSE_CURSOR (cur);
END;
12/08/21 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;
12/08/21 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;
12/08/21 Copyright
PL/SQL Advanced Techniques - page 97
Dynamic Formula Execution
Suppose I am building a user interface that allows a user to
select a formula for execution, and enter the arguments.
– Using static PL/SQL, I would have to modify my screen every time a
new formula was added.
– With DBMS_SQL, a single function will do the trick.
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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 98
More on Dynamic PL/SQL
BEGIN
cur := open_and_parse
('BEGIN get_max_sal (:deptin, :salout); END;');
12/08/21 Copyright
PL/SQL Advanced Techniques - page 101
Working with LONG Values
DBMS_SQL provides special procedures so that you can
extract values from a LONG column in a table.
– DBMS_SQL.DEFINE_COLUMN_LONG
– DBMS_SQL.COLUMN_VALUE_LONG
First, you define the column as a LONG,
Then you retrieve the column using the special
COLUMN_VALUE_LONG variant.
– Transfer the LONG contents into an index-by table so that you can
transfer a value of more than 32K bytes into your PL/SQL program.
dumplong.pkg
dumplong.tst
12/08/21 Copyright
PL/SQL Advanced Techniques - page 102
New DBMS_SQL Features
PL/SQL8
Extensions to
DBMS_SQL
12/08/21 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.
12/08/21 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);
12/08/21 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.
12/08/21 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.
12/08/21 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
12/08/21 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!
12/08/21 Copyright
PL/SQL Advanced Techniques - page 109
Achieving PL/SQL Excellence
Oracle
Advanced Queuing
The high performance, asynchronous,
persistent messaging subsytem of
Oracle
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 111
Applications Relying on Messaging
Stock trading system
Auction portals
12/08/21 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.
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 115
AQ Components
The DBMS_AQ package offers enqueue and
dequeue capabilities
The DBMS_AQADM package provides
administrative functionality to manage queues and
queue tables.
Underlying database tables and views
The queue monitor (background process)
– Set # of processes with the AQ_TM_PROCESSES
initialization parameter.
12/08/21 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
12/08/21 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
12/08/21 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.
12/08/21 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;
12/08/21 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;
12/08/21 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);
12/08/21 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".
12/08/21 Copyright
PL/SQL Advanced Techniques - page 125
Achieving PL/SQL Excellence
Managing
Large Objects
with DBMS_LOB
12/08/21 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
Possible applications include office documents, Images,
sounds, video, etc.
LOB datatypes are the successor to LONGs.
– Oracle has announced deprecation of LONGs; they should no
longer be used, though they will probably not be actually de-
supported.
12/08/21 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 varying
width in )
Temporary LOBs
– 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
12/08/21 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
12/08/21 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);
<lob segment>
12/08/21 Copyright
PL/SQL Advanced Techniques - page 131
Example: Piecewise CLOB programmatic insert
Create a table 1 of 2
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;
...
12/08/21 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>';
12/08/21 Copyright
PL/SQL Advanced Techniques - page 135
Using BFILE datatype in DDL
In a table...
In an object type...
12/08/21 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
12/08/21 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
12/08/21
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
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 140
Achieving PL/SQL Excellence
Leveraging Java
Inside PL/SQL
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 142
Question 1: What is Java?
Could it be...
– The end of programming history as we know it?
– The easiest, fastest, slickest piece of software ever designed by
human beings?
– Just the latest in a series of "silver bullets" promoted by software
vendors in order to prop up quarterly sales?
– The first and only successful O-O language?
– None of the above?
12/08/21 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.
12/08/21 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.
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 exception 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.
12/08/21 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!");
}
}
12/08/21 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.
12/08/21 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:
d:\java> java Hello
12/08/21 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.
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 152
Language Basics
Comments // Single line comment
/* Block
comment */
12/08/21 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()); }
}
12/08/21 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
}
12/08/21 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:
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 158
Java Stored Procedures (JSPs)
Java applet or
app. using
JDBC or
SQLJ Oracle 8i server
12/08/21 Copyright
PL/SQL Advanced Techniques - page 160
Creating JSP to call from PL/SQL
1. Create Java classes in your favorite IDE
2. Load into server using “loadjava” command-line
tool
3. Publish PL/SQL cover using AS LANGUAGE
JAVA... rather than BEGIN...END
4. Grant privileges as desired
5. Call from PL/SQL (or SQL) as if calling PL/SQL
12/08/21 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 to
" is a transnational entity with " + layoffs + test the class.
" laid-off employees, paying its" +
" Chief Executive Officer " + CEOCompensation; Entry points must be
} public static in most
public static void main (String[] args) { cases
// A very scary company
Corporation TheGlobalMonster =
Classes may call other
new Corporation ( classes
"Northrup-Ford-Mattel-Yahoo-ATT",
5000000, 50000000); Avoid GUI calls
System.out.println (TheGlobalMonster);
12/08/21
}} 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;
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 166
Publish in PL/SQL Package Spec
Designate Java module in PL/SQL package spec...
12/08/21 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;
/
12/08/21 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
12/08/21 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
12/08/21 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.
12/08/21 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).
12/08/21 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
12/08/21 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.
12/08/21 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.
12/08/21 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;
12/08/21 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
12/08/21 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.
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).
12/08/21 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
getErrInfo.sp
ORA-29532: Java call terminated by uncaught Java exception: java.sql.SQLException:
ORA-00942: table or view does not exist dropany2.tst
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 186
Process & data flow
Application Net8 External
Shared
Makes Library
External
External
request
Procedure
Procedure
PL/SQL Runtime Listener
Engine Listener calls
routine
Routine
Routineinin
Calls spawns
Client .DLL
.DLLoror.so
Clientoror PL/SQL
from .so
server-side PL/SQL file
file
server-side body
application
application body
returns extproc
extproc
results returns
returns results
results
12/08/21 Copyright
PL/SQL Advanced Techniques - page 187
External procedures: Sample uses
Send email email.sql
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 189
Package spec (2/4)
Nothing unexpected here:
END disk_util;
/
12/08/21 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);
12/08/21 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);
12/08/21 Copyright
PL/SQL Advanced Techniques - page 193
1. Identify/create external routine
Prerequisite: O/S must support shared libraries
12/08/21 Copyright
PL/SQL Advanced Techniques - page 194
2. Create the Oracle library
Syntax:
CREATE OR REPLACE LIBRARY <library name>
AS
'<full path to file>';
12/08/21 Copyright
PL/SQL Advanced Techniques - page 195
3. Map the parameters
12/08/21 Copyright
PL/SQL Advanced Techniques - page 196
Achieving PL/SQL Excellence
Autonomous Transactions
Invoker Rights Model
Row Level Security
12/08/21 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
12/08/21 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.
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.
12/08/21 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.
Call functions within SQL that change the database.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 200
Logging with ATs
Don't forget that ROLLBACK in the exception section!
12/08/21 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
12/08/21 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 …
12/08/21 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
12/08/21 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
Sure would be nice to have a choice...and now you do!
12/08/21 Copyright
PL/SQL Advanced Techniques - page 205
Oracle8i Invoker Rights Syntax
For top level modules:
12/08/21 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.
Central
Central Code
Code schema
schema User/Data
User/Data schema
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...
12/08/21 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
12/08/21 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
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 210
Row Level Security
DBMS_RLS
Row-Level
Security
12/08/21 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").
Let's step through a simple example to see how all these pieces tie
together.
12/08/21 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
12/08/21 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;
12/08/21 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.
12/08/21 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;
12/08/21 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
12/08/21 Copyright
PL/SQL Advanced Techniques - page 218
Queries Transparently Filtered
What you see is determined automatically by who you are.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 219
Take Full Advantage of PL/SQL!
January... February... March... April... May...
12/08/21 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
12/08/21 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
12/08/21 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.
12/08/21 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.
12/08/21 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.
The NO_DATA_FOUND exception is raised if you read past
the end of the file.
– You might want to build your own GET_LINE which handles the
exception and returns an EOF Boolean status flag.
getnext.sp
12/08/21 Copyright
PL/SQL Advanced Techniques - page 228
Writing to a File
DECLARE
fid UTL_FILE.FILE_TYPE;
BEGIN
fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'W');
UTL_FILE.PUT_LINE (fid, 'UTL_FILE');
UTL_FILE.PUT (fid, 'is so much fun');
UTL_FILE.PUTF (fid, ' that I never\nwant to %s', '&1');
UTL_FILE.FCLOSE (fid);
END;
UTL_FILE
is so much fun that I never Resulting Text
want to stop
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.
12/08/21 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;
12/08/21 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.
12/08/21 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
12/08/21 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.
12/08/21 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.
12/08/21 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;
A job is a call to a stored procedure or an anonymous block
– It must end with a semi-colon and can contain “hard-coded” arguments.
When you submit a job, you specify the date on which it should next execute, and then the job’s execution
interval (frequency of execution).
– In the above example, I run calculate_totals immediately and then on a daily basis thereafter. Notice that the start time
is a DATE expression, while the interval is a string (this is dynamic PL/SQL!)
You can also call DBMS_JOB.ISUBMIT and supply the job number, instead of having DBMS_JOB generate one
for you.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 236
Submit Job Example
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)'
);
p.l (v_jobno);
END;
/
12/08/21 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.
12/08/21 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
DBMS_JOB.RUN (my_job#);
Export jobs from the queue.
– Produces a string that can be used to recreate an existing job in the job queue.
– Uses DBMS_JOB.ISUBMIT, retaining current job number.
FROM DBA_JOBS_RUNNING jr
,DBA_JOBS j
,V$SESSION s
12/08/21 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.
12/08/21 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.
12/08/21 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.
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 245
Built-in Packages: DBMS_PIPE
DBMS_PIPE
Inter-session
Communication
12/08/21 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.
12/08/21 Copyright
PL/SQL Advanced Techniques - page 247
Architecture of DBMS_PIPE
Shared Global Area
“Sue”
“Bob”
Session A Session B
A pipe is a named object that uses the System Global Area
to provide a non-transaction based conduit of information.
– The pipe sends/receives a message, which can be composed of one
or more separate packets, using a maximum of 4096 bytes.
– Names can be up to 128 chars long (do not use names beginning
with “ORA$”. They are reserved for Oracle use).
12/08/21 Copyright
PL/SQL Advanced Techniques - page 248
Processing Flow of DBMS_PIPE
Construct a message from packets of information.
– Each packet can be a string, date, number, ROWID or RAW.
– There is just one message buffer per session.
Send the message to a named pipe.
– If there isn’t currently room in the pipe, you can wait for up to
1000 days (or 86400000 seconds) for the pipe to be cleared.
– This is the default, so you should always specify a timeout period.
Receive a message from that pipe.
– You can wait for up to 1000 days for a message.
Unpack the message packets and take action.
– Separate out the individual packets in the message (again: string,
date or number).
pipex1.sql
12/08/21 Copyright pipex2.sql
PL/SQL Advanced Techniques - page 249
DBMS_PIPE Module Outline
Module Name Description
CREATE_PIPE Creates a PUBLIC or PRIVATE pipe.
NEXT_ITEM_TYPE Returns the datatype of the next item in the piped message.
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.
12/08/21 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;
12/08/21 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.
12/08/21 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"!).
12/08/21 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
12/08/21 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.
12/08/21 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;
12/08/21 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
12/08/21 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
12/08/21 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
12/08/21 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.
12/08/21 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;
12/08/21 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
12/08/21 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
12/08/21 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.
next_line := SUBSTR (
stk, startpos, next_newline - startpos + 1);
showcomp.sp
snc.pkg
12/08/21 Copyright
PL/SQL Advanced Techniques - page 269