You are on page 1of 202

21st Century PL/SQL

Blast off to the


faster, smarter, easier world
of Oracle PL/SQL
in Oracle10g and Oracle11g

Steven Feuerstein
PL/SQL Evangelist, Quest Software
www.ToadWorld.com/SF

Copyright 2000-2007 Steven Feuerstein - Page 1


Two days of the newest, best, most
important features of Oracle PL/SQL
 The new PL/SQL Compiler
– Optimizing compiler, compile-time warnings, conditional
compilation
 Collections
– String-based indexes, multi-level collections, high level set
operations for nested tables, table functions, and more.
 High Performance PL/SQL
– A roundup of key features that will help you speed up
performance of your PL/SQL applications
 Other Oracle 11g New Features
Continued....
Copyright 2000-2007 Steven Feuerstein - Page 2
More of the newest, best, most important
features of Oracle PL/SQL
 Dynamic SQL
– Overview, Dynamic PL/SQL, method 4 dynamic SQL, when
and how to use DBMS_SQL.
 Table Functions
– Improve performance, hide complexity
 Object types in PL/SQL
– For the object-oriented in all of us...
 Handy built-in package functionality
– Read and write files; Random value generation; Send email,
recompile code; Miscellaneous wonderful capabilities in
DBMS_OUTPUT and DBMS_UTILITY.
Copyright 2000-2007 Steven Feuerstein - Page 3
So...why listen to me?
Because I am a programmer obsessed...
And I build production applications....

Copyright 2000-2007 Steven Feuerstein - Page 4


Enough about me. What about you?

 How many years working on Oracle?


 What version of Oracle are you using?
 Are you a developer, DBA, or both?
 Do you write frontend code, too? What sort?
 Do we have any dev mgrs or team leads?
 Are you writing new applications, maintaining
existing apps or customizing OTS software?
 What IDE do you use to write code?
– Anybody still using ONLY SQL*Plus + editor?

Copyright 2000-2007 Steven Feuerstein - Page 5


How to benefit most from this session
 Watch, listen, focus on concepts and principles.
 Download and use any of my the training materials:
PL/SQL Obsession http://www.ToadWorld.com/SF

 Download and use any of my scripts (examples,


performance scripts, reusable code) from the
same location: the demo.zip file. filename_from_demo_zip.sql

 You have my permission to use all these materials to


do internal trainings and build your own applications.
– But remember: they are not production ready.
– Modify them to fit your needs and then test them!
Copyright 2000-2007 Steven Feuerstein - Page 6
First, some PL/SQL fundamentals

 The STANDARD package and how to be a


PL/SQL sleuth
 The PL/SQL run-time memory architecture
 Invoker rights
– Running code under the current user's authority
 Autonomous transactions
– Control the scope of commits and rollbacks

Copyright 2000-2007 Steven Feuerstein - Page 7


The STANDARD Package and
the Rdbms/Admin Directory
 Much of what we consider to be the base
PL/SQL language is defined in the STANDARD
package.
– One of two "default" packages; the other is
DBMS_STANDARD.
 You can view the contents of the STANDARD
package (and other "supplied" packages by
visiting the appropriate variation of:
$ORACLE_HOME/Rdbms/Admin
standard_demo.sql
Copyright 2000-2007 Steven Feuerstein - Page 8
Code and Data in Shared Memory
 PL/SQL is an interpretative language. The source
code is “partially compiled” into an intermediate form
(“p-code”).
– The p-code is loaded into the shared pool when any
element of that code (package or stand-alone program) is
referenced.
 The partially-compiled code is shared among all
users who have EXECUTE authority on the
program/package.
 Each user (Oracle session) has its own copy of any
data structures defined within the program/package.
– Distinct sets of in-memory data (not shared among
different users) are stored in the Process Global Area.
Copyright 2000-2007 Steven Feuerstein - Page 9
PL/SQL in Shared Memory
System Global Area (SGA) of RDBMS
Instance
Shared Pool Library cache
Shared SQL
Select * Update emp
Reserved Pool
Pre-parsed from emp Set sal=...

Large Pool calc_totals show_emps upd_salaries

emp_rec emp%rowtype; emp_rec emp%rowtype;


Session 1 tot_tab tottabtype; tot_tab tottabtype;

Session 1 memory Session 2 memory Session 2


(PGA/UGA) (PGA/UGA) mysess.pkg
sess2.sql
show_memory.sp
Copyright 2000-2007 Steven Feuerstein - Page 10
Execution Mode Options for PL/SQL

 Review of definer rights


 Introduction of invoker rights
 A look at some of the nuances

Copyright 2000-2007 Steven Feuerstein - Page 11


Two options for resolving data references

 Definer Rights
– Whenever you executed a stored program, it runs
under the privileges of the schema in which the
program was compiled or defined.
 Invoker Rights
– Oracle resolves all data references (table, view,
etc.) at run-time, based on the currently-connect
user and its privileges (directly granted or available
through roles).

Copyright 2000-2007 Steven Feuerstein - Page 12


About Definer Rights
 Allows you to centralize OE Code
Sam_Sales
Order_Mgt
access to and control of
Place
underlying data Close Old
Orders
structures. Cancel

 Ignores roles and relies


on directly-granted
privileges.
OE Data X
Cannot alter
 But it can be a source of Orders table directly.
confusion and
architectural problems.
Note: Oracle built-in packages have
long had the capability of running
under the invoker's authority.

Copyright 2000-2007 Steven Feuerstein - Page 13


Problems with Definer Rights

 Deployment & maintenance


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

Copyright 2000-2007 Steven Feuerstein - Page 14


Invoker Rights Syntax

 For top level modules:

CREATE [ OR REPLACE ] <module type>


[ AUTHID { DEFINER | CURRENT_USER } ]
AS ...

 For modules with separate spec and body,


AUTHID goes only in spec, and must be at
the package level.
– Holds true for packages and object types.

Copyright 2000-2007 Steven Feuerstein - Page 15


"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...

Copyright 2000-2007 Steven Feuerstein - Page 16


When Invoker Rights Applies

 Resolution against invoker's privileges is made


for these statements:
– SELECT, INSERT, UPDATE, and DELETE data
manipulation statements
– The LOCK TABLE transaction control statement
– OPEN and OPEN-FOR cursor control statements
– All dynamic SQL statements
 For all other statements, resolution is by the
owner's privileges.
– This includes ALL code references.
– Ah, but perhaps there is a way around this restriction!

Copyright 2000-2007 Steven Feuerstein - Page 17


Roles and Privileges

 With definer rights, roles are disabled and


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

invrole.sql

Copyright 2000-2007 Steven Feuerstein - Page 18


Compiling with "Template" Objects

 When writing code with the intention of relying


on invoker rights, the data object referenced
may not be present in the code's schema.
– You need some kind of "template" against which to
successfully compile the code.
 Two options; either create a...
– Synonym to any of the possible resolved objects.
– Local, "dummy" object to allow the code to compile,
knowing that it will never be used at run-time.

Copyright 2000-2007 Steven Feuerstein - Page 19


Invoker-Definer Precedence

 If the first program in the execution stack is defined


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

invdefinv.sql
invdefinv.tst
irdynsql.sql
invoker_rights_mode.sf
Copyright 2000-2007 Steven Feuerstein - Page 20
Invoker Rights and the PL/SQL Call Stack

 As mentioned earlier, the call stack (A called B


calls C) is frozen at the time of compilation.
– The AUTHID CURRENT_USER clause does not
apply - at least not directly
 But you can achieve the same effect by
embedding your PL/SQL program call within a
dynamic PL/SQL statement.
– Then the call to the program is resolved at runtime.
– And AUTHID CURRENT_USER will be applied to
that statement!
invoker_plsql.sql

Copyright 2000-2007 Steven Feuerstein - Page 21


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.
 You can now define a PL/SQL block to execute as an
"autonomous transaction".
– Any changes made within that block can be saved or reversed without
affecting the outer or main transaction.

CREATE OR REPLACE PROCEDURE loginfo (


code IN PLS_INTEGER,
msg IN VARCHAR2)
AS
PRAGMA AUTONOMOUS_TRANSACTION;

Copyright 2000-2007 Steven Feuerstein - Page 22


When to Use Autonomous Transactions

 Reusable Application Components


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

Copyright 2000-2007 Steven Feuerstein - Page 23


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

 An AT program that executes DML must COMMIT or


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

 Avoid mutating table error for triggers that


only query from the mutating table by making
the trigger an autonomous transaction.
– But not that you will not be able to see the
changes to that row from the trigger.
 Watch out for deadlocks!
– If you change programs that formerly assumed
shared locks to ATs, then you might suddenly
experience deadlocks.

Copyright 2000-2007 Steven Feuerstein - Page 27


Autonomous Transactions - Summary

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

 You should immediately explore


opportunities to utilize this feature.

Copyright 2000-2007 Steven Feuerstein - Page 28


The New PL/SQL Compiler

 Optimizing compiler
– Recompile in 10g and experience 100% improvement in
performance (results may vary).
 Compile-time warnings
– Now the PL/SQL compiler tells you more than simply
compilation errors.
 Conditional compilation (10.2 only)
– "ifdef" like pre-processing for PL/SQL
 Automated in-lining of local subprograms
– New to Oracle11g

Copyright 2000-2007 Steven Feuerstein - Page 29


Oracle10g Wow! An optimizing compiler!
 Yes, the PL/SQL compiler now has the ability to
automatically optimize your code.
– Possible rearrangements to the code itself (under the covers).
 You can choose the level of optimization through the
plsql_optimize_level setting:
– 2 Most aggressive, maximum possible code transformations,
biggest impact on compile time. [default]
– 1 Smaller scale change, less impact on compile times
– 0 Pre-10g compilation without optimization

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 1;

10g_optimize_cfl.sql

Copyright 2000-2007 Steven Feuerstein - Page 30


Learn about the PL/SQL optimizer

http://www.oracle.com/technology/tech/pl_sql/htdocs/new_in_10gr1.htm

 PL/SQL Just Got Faster


– Explains the workings of the PL/SQL compiler and runtime system and
shows how major improvements on this scale are indeed possible.
 PL/SQL Performance Measurement Harness
– Describes a performance experiment whose conclusion is the large
factors quoted above. We’ve provided a downloadable kit to enable you to
repeat the experiment yourself.
 Freedom, Order, and PL/SQL Optimization
– Intended for professional PL/SQL programmers, explores the use and
behavior of the new compiler.
 PL/SQL Performance — Debunking the Myths
– Re-examines some old notions about PL/SQL performance.
Copyright 2000-2007 Steven Feuerstein - Page 31
Optimizing compiler details

 Oracle retains optimizer settings on a


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

 and then:
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;

Copyright 2000-2007 Steven Feuerstein - Page 32


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

REM To enable all warnings in your session execute:


ALTER SESSION SET plsql_warnings = 'enable:all‘;

REM If you want to enable warning message number 06002 and all warnings in
REM the performance category, and treat warning 5005 as a "hard" compile error:
ALTER SESSION SET plsql_warnings =
'enable:06002', 'enable:performance', 'ERROR:05005';

Copyright 2000-2007 Steven Feuerstein - Page 33


Compiler time warnings - example
 Check for “unreachable end” code….
SQL> CREATE OR REPLACE PROCEDURE unreachable_code IS
2 x NUMBER := 10;
3 BEGIN
4 IF x = 10 THEN
5 x := 20;
6 ELSE
7 x := 100; -- unreachable code
8 END IF;
9 END unreachable_code;
10 /
 
SP2-0804: Procedure created with compilation warnings
 
SQL> show err
Errors for PROCEDURE UNREACHABLE_CODE:
 
LINE/COL ERROR plw*.sql
-------- -------------------------------------
7/7 PLW-06002: Unreachable code

Copyright 2000-2007 Steven Feuerstein - Page 34


Conditional Compilation in 10g Release 2

 Compile selected parts of a program based on


conditions you provide with various compiler
directives.
– The most significant new feature for PL/SQL developers.
 Conditional compilation will allow you to:
– Write code that will compile and run under different
versions of Oracle (relevant for future releases).
– Run different code for test, debug and production phases.
That is, compile debug statements in and out of your code.
– Expose private modules for unit testing.

Copyright 2000-2007 Steven Feuerstein - Page 35


Inquiry directive example

 Set up conditional compilation of debugging and


tracing with special "CC" flags that are placed into
the compiler settings for a program.
ALTER SESSION SET PLSQL_CCFLAGS = 'oe_debug:true, oe_trace_level:10';

CREATE OR REPLACE PROCEDURE calculate_totals


IS
BEGIN
$IF $$oe_debug AND $$oe_trace_level >= 5
$THEN
DBMS_OUTPUT.PUT_LINE ('Tracing at level 5 or higher');
$END
NULL;
END calculate_totals;
/

cc_debug_trace.sql

Copyright 2000-2007 Steven Feuerstein - Page 36


DBMS_DB_VERSION

 Each version of Oracle from Oracle Database 10g


Release 2 will contain a DBMS_DB_VERSION
package.
– Contains a set of Boolean constants that tell you how this
version relates to other Oracle versions.
– Here is an example:
$IF DBMS_DB_VERSION.VER_LE_10_2
$THEN
Use this code.
$ELSEIF DBMS_DB_VERSION.VER_LE_11
This is a placeholder for future.
$ENDIF

 This will be most beneficial when we can use it in


multiple versions of Oracle!

Copyright 2000-2007 Steven Feuerstein - Page 37


11g Oracle11g In-lining optimization
 When you set the optimization level to 3, the
compiler will automatically "in-line" the code for any
local modules for which the operation is specified.
– A subprogram that is defined in the declaration section of
that program.
 Oracle's own tests have shown 10-20%
performance improvement.
– Depends on many local modules you create and how
often they are used.
 Note: compile code size increases.
11g_inline.sql

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 3;

Copyright 2000-2007 Steven Feuerstein - Page 38


11g Inlining optimization, continued
 Inlining must be explicitly required in your code with a new
INLINE pragma.
 Inlining applies to the following statements:
– Assignment, CALL, conditional, CASE, CONTINUE-WHEN,
EXECUTE IMMEDIATE, EXIT-WHEN, LOOP, RETURN
 You can also request inlining for all executions of the
subprogram by placing the PRAGMA before the declaration of
the subprogram.
 Inlining, like NOCOPY, is a request.
– Under some circumstances, inlining will not take place.
 You can also explicitly turn off inlining by specifying "NO" in
the PRAGMA.
11g_inline.sql
Copyright 2000-2007 Steven Feuerstein - Page 39
Compiler Improvements - Summary

 Optimizer
– Go with the defaults!
 Compile-time warnings
– Try them out, see how much value you can extract from
it.
 Conditional compilation
– Lots of potential
– Smart tool support needed to make it feasible and
maintainable
 Automatic inlining
– Useful, but probably in a relatively limited way

Copyright 2000-2007 Steven Feuerstein - Page 40


PL/SQL Collections

 Collections are single-dimensioned lists of


information, similar to 3GL arrays.
 They are an invaluable data structure.
– All PL/SQL developers should be very comfortable
with collections and use them often.
 Collections take some getting used to.
– They are not the most straightforward
implementation of array-like structures.
– Advanced features like string indexes and multi-
level collections can be a challenge.

Copyright 2000-2007 Steven Feuerstein - Page 41


What we will cover on collections

 Review of basic functionality


 Indexing collections by strings
 Working with collections of collections
 MULTISET operators for nested tables
 Then later in the section on high
performance PL/SQL:
– Bulk processing with FORALL and BULK
COLLECT
– Table functions and pipelined functions

Copyright 2000-2007 Steven Feuerstein - Page 42


What is a collection?
1 2 3 4 22 23
abc def sf q ... rrr swq

 A collection is an "ordered group of elements,


all of the same type." (PL/SQL User Guide and
Reference)
– That's a very general definition; lists, sets, arrays and similar
data structures are all types of collections.
– Each element of a collection may be addressed by a unique
subscript, usually an integer but in some cases also a string.
– Collections are single-dimensional, but you can create
collections of collections to emulate multi-dimensional
structures.

Copyright 2000-2007 Steven Feuerstein - Page 43


Why use collections?

 Generally, to manipulate in-program-memory lists of


information.
– Much faster than working through SQL.
 Serve up complex datasets of information to non-
PL/SQL host environments using table functions.
 Dramatically improve multi-row querying, inserting,
updating and deleting the contents of tables.
– Combined with BULK COLLECT and FORALL....
 Emulate bi-directional cursors, which are not yet
supported within PL/SQL.
bidir.*

Copyright 2000-2007 Steven Feuerstein - Page 44


Three Types of Collections

 Associative arrays (aka index-by tables)


– Can be used only in PL/SQL blocks.
– Similar to hash tables in other languages, allows you to
access elements via arbitrary subscript values.
 Nested tables and Varrays
– Can be used in PL/SQL blocks, but also can be the
datatype of a column in a relational table.
– Part of the object model in PL/SQL.
– Required for some features, such as table functions
– With Varrays, you specify a maximum number of elements
in the collection, at time of definition.

Copyright 2000-2007 Steven Feuerstein - Page 45


About Associative Arrays

 Unbounded, practically speaking.


– Valid row numbers range from -2,147,483,647 to
2,147,483,647.
– This range allows you to employ the row number as an
intelligent key, such as the primary key or unique index
value, because AAs also are:
 Sparse
– Data does not have to be stored in consecutive rows, as is
required in traditional 3GL arrays and VARRAYs.
 Index values can be integers or strings (Oracle9i R2
and above).
assoc_array_example.sql
Copyright 2000-2007 Steven Feuerstein - Page 46
About Nested Tables

 Name reflects fact that this collection can be


"nested" inside relational table as a column.
 Type can be defined at schema level.
 No practical, pre-defined limit on a nested table.
– Valid row numbers range from 1 to 2,147,483,647.
 Part of object model, requiring initialization.
 Is always dense initially, but can become sparse
after deletes.

nested_table_example.sql
Copyright 2000-2007 Steven Feuerstein - Page 47
About Varrays

 Has a maximum size, associated with its type.


– Can adjust the size at runtime in Oracle10g R2.
 Part of object model, requiring initialization.
 Is always dense; you can only remove
elements from the end of a varray.
 Can be defined as a schema level type and
used as a relational table column type.

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

Copyright 2000-2007 Steven Feuerstein - Page 49


Useful reminders for PL/SQL collections

 Memory for collections comes out of the PGA or


Process Global Area
– One per session, so a program using collections can
consume a large amount of memory.
 Use the NOCOPY hint to reduce overhead of passing
collections in and out of program units.
 Encapsulate or hide details of collection management.
 Don't always fill collections sequentially. Think about
how you need to manipulate the contents.
 Try to read a row that doesn't exist, and Oracle raises
NO_DATA_FOUND.
mysess.pkg
sess2.sql
nocopy*.*
Copyright 2000-2007 Steven Feuerstein - Page 50
Apply PL/SQL Collections

 We will take a look at the following


application of PL/SQL collections:
– Caching data in the PGA with collections
 Then we will explore advanced features of
collections.
– String-indexed collections
– Multi-level collections

Copyright 2000-2007 Steven Feuerstein - Page 51


Data Caching with PL/SQL Collections

 Manipulating memory is much more efficient than


reading/writing hard drives.
– That's the whole point of the SGA.
 But not all memory is created (and managed) the
same. So it would seem to make sense that...
 Manipulating faster memory will be more efficient than
manipulating slow memory.
 PGA memory is faster than SGA memory.
– Don't have to go through the SQL layer.
– So how can we take advantage of this fact?

Copyright 2000-2007 Steven Feuerstein - Page 52


Data Caching with PL/SQL Collections
Data retrieved
First access Pass Data from cache Data returned
to Cache to application

Database Application
/ SGA Function
Not in cache; PGA
Request data
from database Application
Requests Data

Subsequent accesses Data retrieved Data returned


from cache
to application

Data found in
Database cache. Database Application
is not needed.
/ SGA Function
PGA
Application
Requests Data
emplu.pkg
emplu.tst
Copyright 2000-2007 Steven Feuerstein - Page 53
PGA Caching: Things to keep in mind

 Must use package-level data so that it persists.


– Memory is consumed by the PGA and so is multiplied for
all users of the application.
 Cache cannot be shared across sessions.
– Or at least not very easily/practically.
– If using connection pooling, must think through the
consequences.
 Useful under specific scenarios....
– Small, static dataset
– Single or small number of batch processes
syscache.pkg

Copyright 2000-2007 Steven Feuerstein - Page 54


New indexing capabilities
Oracle9i Release 2
for associative arrays
 Prior to Oracle9iR2, you could only index by
BINARY_INTEGER.
 You can now define the index on your associative
array to be:
– Any sub-type derived from BINARY_INTEGER
– VARCHAR2(n), where n is between 1 and 32767
– %TYPE against a database column that is consistent with
the above rules
– A SUBTYPE against any of the above.
 This means that you can now index on string
values! (and concatenated indexes and...)

Copyright 2000-2007 Steven Feuerstein - Page 55


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

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

Copyright 2000-2007 Steven Feuerstein - Page 56


Working with string-indexed collections

 The syntax is exactly the same, but you should keep


this in mind:
– The datatype returned by FIRST, LAST, NEXT and PRIOR
methods is VARCHAR2.
– The longer the string values, the more time it takes Oracle
to hash that string to the integer that is actually used as the
index value.
 If you are indexing by integer and find that your
values are getting close to the limits (2**31 - 1 or
-2**31 + 1), convert to a string index.
assoc_array*.sql
assoc_array_perf.tst
int_to_string_indexing.sql
Copyright 2000-2007 Steven Feuerstein - Page 57
The String Tracker package (V1)

 Another example: I need to keep track of the


names of variables that I have already used in
my test code generation.
– Can't declare the same variable twice.
CREATE OR REPLACE PACKAGE BODY string_tracker
IS
TYPE used_aat IS TABLE OF BOOLEAN INDEX BY maxvarchar2_t;
g_names_used used_aat;

FUNCTION string_in_use ( value_in IN maxvarchar2_t ) RETURN BOOLEAN


IS BEGIN
RETURN g_names_used.EXISTS ( value_in );
END string_in_use;

PROCEDURE mark_as_used (value_in IN maxvarchar2_t) IS


BEGIN
g_names_used ( value_in ) := TRUE; string_tracker1.*
END mark_as_used;
END string_tracker;

Copyright 2000-2007 Steven Feuerstein - Page 58


Rapid access to data via strings

 One of the most powerful applications of string-


indexed collections is to construct very fast
pathways to static data from within PL/SQL
programs.
– If repeatedly querying the same data from the database,
why not cache it in your PGA inside collections?
 Emulate the various indexing mechanisms (primary
key, unique indexes) with collections.

Generate a caching package: Comparison of performance


Demonstration package:
genaa.sql of different approaches:
assoc_array5.sql
genaa.tst vocab*.*

Copyright 2000-2007 Steven Feuerstein - Page 59


Oracle9i Multi-level Collections

 Prior to Oracle9i, you could have collections of


records or objects, but only if all fields were
scalars.
– A collection containing another collection was not
allowed.
 Now you can create collections that contain
other collections and complex types.
– Applies to all three types of collections.
 The syntax is non-intuitive and resulting code
can be quite complex.
Copyright 2000-2007 Steven Feuerstein - Page 60
String Tracker Version 2

 The problem with String Tracker V1 is that it


only supports a single list of strings.
– What if I need to track multiple lists
simultaneously or nested?
 Let's extend the first version to support
multiple lists by using a string-indexed, multi-
level collection.
– A list of lists....

Copyright 2000-2007 Steven Feuerstein - Page 61


The String Tracker package (V2)
CREATE OR REPLACE PACKAGE BODY string_tracker
IS
TYPE used_aat IS TABLE OF BOOLEAN INDEX BY maxvarchar2_t;
TYPE list_of_lists_aat IS TABLE OF used_aat INDEX BY maxvarchar2_t;
g_list_of_lists list_of_lists_aat;

PROCEDURE mark_as_used (
list_in IN maxvarchar2_t
, value_in IN maxvarchar2_t
, case_sensitive_in IN BOOLEAN DEFAULT FALSE
) IS
l_name maxvarchar2_t :=
CASE case_sensitive_in WHEN TRUE THEN value_in
ELSE UPPER ( value_in ) END;
BEGIN
g_list_of_lists ( list_in ) ( l_name ) := TRUE;
END mark_as_used;
END string_tracker;

string_tracker2.*
string_tracker3.*
Copyright 2000-2007 Steven Feuerstein - Page 62
Other multi-level collection examples

 Multi-level collections with intermediate records


and objects. multilevel_collections.sql

 Emulation of multi-dimensional arrays


– No native support, but can creates nested
collections to get much the same effect.
– Use the UTL_NLA package (10gR2) for complex
matrix manipulation. multdim*.*

 Four-level nested collection used to track


arguments for a program unit.
– Automatically analyze ambiguous overloading.
ambig_overloading.sql
OTN: OverloadCheck
Copyright 2000-2007 Steven Feuerstein - Page 63
Encapsulate these complex structures!
 When working with multi-level collections, you
can easily and rapidly arrive at completely
unreadable and un-maintainable code.
 What' s a developer to do?
– Hide complexity -- and all data structures -- behind
small modules.
– Work with and through functions to retrieve
contents and procedures to set contents.

cc_smartargs.pkb:
cc_smartargs.next_overloading
cc_smartargs.add_new_parameter

Copyright 2000-2007 Steven Feuerstein - Page 64


Nested Tables unveil their
Oracle10g
MULTISET-edness
 Oracle10g introduces high-level set operations
on nested tables (only).
– Nested tables are “multisets,” meaning that
theoretically there is no order to their elements..
 You can now…
– Check for equality and inequality
– Perform UNION, INTERSECT and MINUS operations
– Check for and remove duplicates
 Only works with nested tables of scalars.

Copyright 2000-2007 Steven Feuerstein - Page 65


Oracle10g Check for equality and inequality
 Just use the basic operators….and NULLs have the
usual disruptive impact.
DECLARE
TYPE clientele IS TABLE OF VARCHAR2 (64);
group1 clientele := clientele ('Customer 1', 'Customer 2');
group2 clientele := clientele ('Customer 1', 'Customer 3');
group3 clientele := clientele ('Customer 3', 'Customer 1');
BEGIN
IF group1 = group2 THEN
DBMS_OUTPUT.put_line ('Group 1 = Group 2');
ELSE
DBMS_OUTPUT.put_line ('Group 1 != Group 2');
END IF;

IF group2 != group3 THEN


DBMS_OUTPUT.put_line ('Group 2 != Group 3');
ELSE
DBMS_OUTPUT.put_line ('Group 2 = Group 3');
END IF;
END;
10g_compare.sql
10g_compare2.sql
Copyright 2000-2007 Steven Feuerstein - Page 66 10g_compare_old.sql
Oracle10g UNION, INTERSECT, MINUS

 Straightforward, with the MULTISET keyword.


BEGIN
our_favorites :=
my_favorites MULTISET UNION dad_favorites; SQL: UNION ALL

our_favorites :=
my_favorites MULTISET UNION DISTINCT dad_favorites; SQL: UNION

our_favorites :=
my_favorites MULTISET INTERSECT dad_favorites; SQL: INTERSECT

our_favorites :=
dad_favorites MULTISET EXCEPT my_favorites; SQL: MINUS
END;

10g_setops.sql
10g_string_nt.sql
10g_favorites.sql
10g*union*.sql
Copyright 2000-2007 Steven Feuerstein - Page 67
Oracle10g Distinct sets of values
 Use the SET operator to work with distinct values, and determine if you have a set
of distinct values.

DECLARE
keep_it_simple strings_nt := strings_nt ();
BEGIN
keep_it_simple := SET (favorites_pkg.my_favorites);

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

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


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

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

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


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

END;

10g_set.sql
10g_favorites.pkg
Copyright 2000-2007 Steven Feuerstein - Page 68
How to choose your collection type
 Use associative arrays when you need to...
– Work within PL/SQL code only
– Sparsely fill and manipulate the collection
– Take advantage of negative index values and string indexing
 Use nested tables when you need to...
– Access the collection inside SQL
– Want to perform set operations
 Use varrays when you need to...
– If you need to (or can) specify a maximum size to your
collection
– Access the collection inside SQL: better performance than
nested tables, since varrays are stored "in line."

Copyright 2000-2007 Steven Feuerstein - Page 69


Collections vs. Global Temporary Tables

 Global temporary tables cut down on the


overhead of working with persistent tables.
– And you can use the full power of SQL, which is
their main advantage over collections.
 GTTs still require interaction with the SGA.
 So collections will still be faster, but they will
use more memory.
– GTTs consume SGA memory.

global_temp_tab_vs_coll.sql

Copyright 2000-2007 Steven Feuerstein - Page 70


Collections: the latest frontier

 Five-plus years ago, many programmers


wrestled with making packages an every-day
part of their PL/SQL coding.
 Today I offer this challenge: learn collections
thoroughly and apply them throughout your
backend code.
– Your code will get faster and in many cases
much simpler than it might have been (though
not always!).
Hands-On Collection seminar
www.oracleplsqlprogramming.com/resources.html

Copyright 2000-2007 Steven Feuerstein - Page 71


>> High Performance PL/SQL

 We always want our programs to run faster.


 But remember the 80-20 rule:
– Most of your code will never be part of a bottleneck, so
don't obsess about optimizing every line of code.
 We will look in this section at...
– Calculating elapsed time
– Data caching options
– Oracle11g features for performance
– BULK COLLECT and FORALL
– Other, less astounding, performance techniques

Copyright 2000-2007 Steven Feuerstein - Page 72


Calculating Elapsed Time of Programs

 Many options for analyzing Oracle performance:


TKPROF, SET TIMING ON, etc.
– But they usually don't offer the granularity I need for my
PL/SQL performance analysis.
 Oracle offers DBMS_UTILITY.GET_TIME and
GET_CPU_TIME (10g) to compute elapsed time down
to the hundredth of a second.
– Can also use SYSTIMESTAMP plvtmr.*
tmr.ot
DECLARE this_user*.*
l_start_time PLS_INTEGER;
BEGIN
l_start_time := DBMS_UTILITY.get_time;
-- Do stuff here!
DBMS_OUTPUT.put_line (DBMS_UTILITY.get_time – l_start_time);
END;
Copyright 2000-2007 Steven Feuerstein - Page 73
Data Caching Options

 Why cache data?


– Because it is static and therefore you want to
avoid the performance overhead of retrieving
that data over and over again.
 Options for caching data:
– The SGA: Oracle does lots of caching for us, but
it is not always the most efficient means.
– Package data structures (PGA)
– Oracle11g Function Result Cache

Copyright 2000-2007 Steven Feuerstein - Page 74


Packaged collection caching
 Prior to Oracle 11g, the best caching option for
PL/SQL programs involved declaring a package-
level data structure.
– It persists for the entire session.
 Very fast and easy to define, but it has its
drawbacks.
– The cache is copied in every session connected to
Oracle. Significant impact on memory usage.
– Very difficult to synchronize cache with changes to
underlying database tables.

emplu*.*
Copyright 2000-2007 Steven Feuerstein - Page 75
11g The Oracle 11g Function Result Cache

 Oracle offers a far superior solution in 11g: the


Function Result Cache.
 By applying a simple RESULT_CACHE clause to
your function, Oracle will:
– Cache the values returned by the function, along with the
input values.
– Return the cached values if the same input values are
provided.
– Share this cache among all sessions in the instance.
– Invalidate the cache when changes are made to dependent
tables.

Copyright 2000-2007 Steven Feuerstein - Page 76


Function Result Cache Example
CREATE OR REPLACE PACKAGE emplu11g
IS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE
RESULT_CACHE;
END emplu11g;

CREATE OR REPLACE PACKAGE BODY emplu11g


IS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE
RESULT_CACHE RELIES_ON (employees)
IS
onerow_rec employees%ROWTYPE; The specification must indicate you
BEGIN are using a result cache.
SELECT * INTO onerow_rec
FROM employees The body specifies the "relines on"
WHERE employee_id = employee_id_in; dependencies, if any.
RETURN onerow_rec;
END onerow;
END emplu11g;

11g_emplu*.*

Copyright 2000-2007 Steven Feuerstein - Page 77


11g Tuning the Result Cache

 Oracle offers a number of ways to manage


the result cache and tune it to your specific
application needs:
 RESULT_CACHE_SIZE initialization
parameter
 DBMS_RESULT_CACHE management
package
 v$RESULT_CACHE_* performance views

Copyright 2000-2007 Steven Feuerstein - Page 78


Look for opportunities to cache!

 Whether you are on 9i, 10g or 11g, you


should always look for opportunities to
cache.
– Are there tables that are always static during
user sessions?
– Are there tables that are queried much more
frequently than they are changed?
 Apply the most appropriate technique, but
don't leave the users waiting...

Copyright 2000-2007 Steven Feuerstein - Page 79


Turbo-charge SQL with
bulk processing statements
 Improve the performance of multi-row SQL operations by
an order of magnitude or more with bulk/array processing
in PL/SQL!
CREATE OR REPLACE PROCEDURE upd_for_dept (
dept_in IN employee.department_id%TYPE
,newsal_in IN employee.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT employee_id,salary,hire_date
FROM employee WHERE department_id = dept_in;
BEGIN
FOR rec IN emp_cur LOOP

adjust_compensation (rec, newsal_in);


Row by row
UPDATE employee SET salary = rec.salary processing:
WHERE employee_id = rec.employee_id;
END LOOP;
simple and
END upd_for_dept; elegant but
inefficient
Copyright 2000-2007 Steven Feuerstein - Page 80
Row by row processing of DML in PL/SQL
Oracle server

PL/SQL Runtime Engine SQL Engine


PL/SQL block
Procedural
statement
FOR rec IN emp_cur LOOP executor
UPDATE employee SQL
SET salary = ... statement
WHERE employee_id =
rec.employee_id;
executor
END LOOP;

Performance penalty
for many “context
switches”

Copyright 2000-2007 Steven Feuerstein - Page 81


Bulk processing with FORALL
Oracle server

PL/SQL Runtime Engine SQL Engine


PL/SQL block
Procedural
FORALL indx IN statement
list_of_emps.FIRST..
list_of_emps.LAST executor
SQL
UPDATE employee
SET salary = ... statement
WHERE employee_id = executor
list_of_emps(indx);

Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Fewer context switches, Update...
same SQL behavior
Copyright 2000-2007 Steven Feuerstein - Page 82
Bulk Processing in PL/SQL

 FORALL
– Use with inserts, updates and deletes.
– Move data from collections to tables.
 BULK COLLECT
– Use with implicit and explicit queries.
– Move data from tables into collections.
 In both cases, the "back back" end processing in the
SQL engine is unchanged.
– Same transaction and rollback segment management
– Same number of individual SQL statements will be
executed.
– But BEFORE and AFTER statement-level triggers only
fire once per FORALL statement.
Copyright 2000-2007 Steven Feuerstein - Page 83
Use BULK COLLECT INTO for Queries
DECLARE
Declare a TYPE employees_aat IS TABLE OF employees%ROWTYPE
collection of INDEX BY BINARY_INTEGER;
records to hold
l_employees employees_aat;
the queried data. BEGIN
SELECT *
BULK COLLECT INTO l_employees
Use BULK FROM employees; bulkcoll.sql
COLLECT to bulktiming.sql
retrieve all rows. FOR indx IN 1 .. l_employees.COUNT
LOOP
process_employee (l_employees(indx));
Iterate through the END LOOP;
END;
collection
contents with a
loop. WARNING! BULK COLLECT will not raise
NO_DATA_FOUND if no rows are found.
Always check contents of collection to confirm that
something was retrieved.
Copyright 2000-2007 Steven Feuerstein - Page 84
Limit the number of rows returned by BULK
COLLECT
CREATE OR REPLACE PROCEDURE bulk_with_limit
(deptno_in IN dept.deptno%TYPE)
IS
CURSOR emps_in_dept_cur IS
SELECT * FROM emp
WHERE deptno = deptno_in;

TYPE emp_tt IS TABLE OF emps_in_dept_cur%ROWTYPE;


emps emp_tt;
BEGIN
OPEN emps_in_dept_cur;
Use the LIMIT clause with the
LOOP
INTO to manage the amount of
FETCH emps_in_dept_cur
memory used with the BULK
BULK COLLECT INTO emps LIMIT 1000;
COLLECT operation.
EXIT WHEN emps.COUNT = 0;
Definitely the preferred approach
in production applications with
process_emps (emps);
large or varying datasets.
END LOOP;
END bulk_with_limit;
bulklimit.sql
Copyright 2000-2007 Steven Feuerstein - Page 85
Use the FORALL Bulk Bind Statement
 Instead of executing repetitive, individual DML statements, you
can write your code like this:
PROCEDURE upd_for_dept (...) IS
BEGIN
FORALL indx IN list_of_emps.FIRST .. list_of_emps.LAST
UPDATE employee
SET salary = newsal_in
WHERE employee_id = list_of_emps (indx);
END;

 Things to be aware of:


– You MUST know how to use collections to use this feature!
– Only a single DML statement is allowed per FORALL.
– New cursor attributes: SQL%BULK_ROWCOUNT returns number of
rows affected by each row in array. SQL%BULK_EXCEPTIONS...
– Prior to Oracle10g, the binding array must be sequentially filled.
– Use SAVE EXCEPTIONS to continue past errors. bulktiming.sql
bulk_rowcount.sql
Copyright 2000-2007 Steven Feuerstein - Page 86 bulkexc.sql
Oracle9i
Better Exception Handling
for Bulk Operations
 Allows you to continue past errors and obtain error
information for each individual operation (for dynamic
and static SQL).

CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t)


IS
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGIN
FORALL indx IN books_in.FIRST..books_in.LAST
Allows processing of all
SAVE EXCEPTIONS
INSERT INTO book values (books_in(indx)); rows, even after an error
EXCEPTION occurs.
WHEN BULK_ERRORS THEN
FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT
LOOP New cursor
log_error (SQL%BULK_EXCEPTIONS(indx)); attribute, a pseudo-
END LOOP; collection
END;

bulkexc.sql
Copyright 2000-2007 Steven Feuerstein - Page 87
DBMS_ERRLOG (Oracle10gR2)
 Allows DML statements to execute against all rows,
even if an error occurs.
– The LOG ERRORS clause specifies how logging should
occur.
– Use the DBMS_ERRLOG package to associate a log
table with DML operations on a base table.
 Much faster than trapping errors, logging, and then
continuing/recovering.
 You should consider using LOG ERRORS with
FORALL (instead of SAVE EXCEPTIONS) so that
you can obtain all error information!
– But there are some differences
dbms_errlog.*
in behavior. save_exc_vc_dbms_errlog.sql
cfl_to_bulk7.sql
Copyright 2000-2007 Steven Feuerstein - Page 88
Bulk Processing Conclusions

 Most important performance tuning feature in


PL/SQL.
– Almost always the fastest way to execute multi-row SQL
operations in PL/SQL.
 You trade off increased complexity of code for
dramatically faster execution.
– But in Oracle Database 10g and above, the compiler will
automatically optimize cursor FOR loops to BULK
COLLECT efficiency.
– No need to convert unless the loop contains DML.
 Watch out for the impact on PGA memory!

Copyright 2000-2007 Steven Feuerstein - Page 89


The NOCOPY hint
 By default, Oracle passes all arguments by
value, not reference.
– This means that OUT and IN OUT arguments
always involve some copying of data.
 With NOCOPY, you turn off the copy process.
– But it comes with a risk: Oracle will not
automatically "rollback" or reverse changes made to
your variables if the NOCOPY-ed program raises an
exception.

nocopy*.*
string_nocopy.*

Copyright 2000-2007 Steven Feuerstein - Page 90


11g The SIMPLE_INTEGER
and real Native Compilation
 Native Compilation
– With PLSQL_CODE_TYPE='Native' ('INTERPRETED‘ is the
default), Oracle will compile PL/SQL code down to machine
code on all chip sets supported by Oracle.
– Use only for production; you can’t debug native code.
– Oracle recommends that you recompile your entire code base
(including STANDARD and built-in packages) using native
compilation!
 The new, faster SIMPLE_INTEGER:
– Has a NOT NULL constraint
– Values wrap, they do not overflow
– Faster than PLS_INTEGER

ALTER SESSION SET PLSQL_CODE_TYPE = 'NATIVE';


Copyright 2000-2007 Steven Feuerstein - Page 91
>> Other Key and Useful Oracle11g Features

 Trigger enhancements: define order and the


compound trigger
 Dynamic SQL interoperability and completeness
 Use sequences in native PL/SQL
 The CONTINUE statement
 Reference fields of records in FORALL
 PL/Scope
 Additional compile-time warnings

Copyright 2000-2007 Steven Feuerstein - Page 92


11g The Compound Trigger
CREATE TRIGGER full_mfe_excuse_transaction
BEFORE UPDATE ON mfe_customers
– Rather than manage COMPOUND TRIGGER
multiple triggers on the ... declare variables here ...

same table, you can BEFORE STATEMENT IS


BEGIN
join all trigger ...
END BEFORE STATEMENT;
operations into a
single compound BEFORE ROW IS
BEGIN
trigger. ...
END BEFORE ROW;
– Avoidance of mutating
trigger errors is now AFTER ROW IS
BEGIN
much simpler and more ...
straightforward. END AFTER ROW;

AFTER STATEMENT IS
BEGIN
...
mutating.sql END AFTER STATEMENT;
11g_compound_mutating.sql END full_mfe_excuse_transaction;
Copyright 2000-2007 Steven Feuerstein - Page 93
11g Specifying order of trigger firing

 Prior to Oracle11g, when you defined more


than one trigger on the same action (e.g.,
"before insert"), there was no guarantee of the
order in which the triggers would fire.
 Now simply include a FOLLOWS clause:
CREATE OR REPLACE TRIGGER after_insert_validate
BEFORE INSERT
ON my_table
FOR EACH ROW
FOLLOWS after_insert_adjust
BEGIN
...
END;

multiple_triggers.sql
trigger_conflict.sql
Copyright 2000-2007 Steven Feuerstein - Page 94
11g Use sequences in native PL/SQL!

 You no longer have to select from dual to get


the next value of a sequence!
– Much more intuitive code
– Improvement in performance

CREATE OR REPLACE TRIGGER employees_bi_trg


BEFORE INSERT
ON employees
FOR EACH ROW
BEGIN
:NEW.employee_id := my_seq.NEXTVAL;
END;
/

11g_native_sequence.sql
Copyright 2000-2007 Steven Feuerstein - Page 95
11g Using CONTINUE in a loop
 You can now tell PL/SQL to terminate execution of the
current loop body and immediately go on to the next
iteration of that loop.

BEGIN
<<outer_loop >>
FOR o_index IN 1 .. my_list.COUNT
LOOP
<<inner_loop>>
FOR i_index IN your_list.FIRST .. your_list.LAST
LOOP
... lots of code
/* Skip the rest of this and the outer
loop if condition is met. */
CONTINUE outer_loop WHEN condition_is_met;
... more inner loop logic
END LOOP inner_loop;
... more outer loop logic
END LOOP outer_loop;
END;
Copyright 2000-2007 Steven Feuerstein - Page 96
11g Reference fields of records in FORALL
 Prior to 11g, you could not reference a field of
a record in FORALL. Instead, you have
SQL> DECLARE
2 TYPE two_columns_rt IS RECORD ( to break out the
3 employee_id employees.employee_id%TYPE data into separate
4 , salary employees.salary%TYPE collections.
5 );
6 TYPE id_name_list_tt IS TABLE OF two_columns_rt INDEX BY PLS_INTEGER;
7 l_list id_name_list_tt; Very irritating.
8 BEGIN
9 SELECT employee_id, salary
10 BULK COLLECT INTO l_list FROM employees;
11 Oracle11g now
12 FOR idx IN 1 .. l_list.COUNT LOOP
13 l_list (idx).salary := apply_cola (l_list (idx).salary);
lets you do this.
14 END LOOP; BUT...
15 It is not
16 FORALL idx IN 1 .. l_list.COUNT
17 UPDATE employees SET salary = l_list (idx).salary
documented!
18 WHERE employee_id = l_list (idx).employee_id;
19 END;
20 /
UPDATE employees SET salary = l_list (idx).salary 11g_field_of_record.sql
*
ERROR at line 17:
PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND
Copyright 2000-2007 Steven Feuerstein - Page 97
11g PL/Scope

 A compiler-driven tool that collects information


about identifiers and stores it in data dictionary
views.
 Use PL/Scope to answer questions like:
– Where if a variable assigned a value in a program?
– What variables are declared inside a given
program?
– Which programs call another program (that is, you
can get down to a subprogram in a package)?
– Find the type of a variable from its declaration.
Copyright 2000-2007 Steven Feuerstein - Page 98
PL/Scope examples
 Enable gathering of PL/Scope data.
ALTER SESSION SET plscope_settings='IDENTIFIERS:ALL'

 Verify PL/Scope setting for a program.


SELECT plscope_settings
FROM user_plsql_object_settings
WHERE NAME = 'PACK1' AND TYPE = 'PACKAGE BODY'

Identify all declarations for the specified set of programs whose names do not start with "L_".

SELECT name, signature, tyhpe


FROM user_identifiers
WHERE name NOT LIKE 'L\_%' ESCAPE '\' AND USAGE = 'DECLARATION'
AND object_name LIKE '&1'

11g_plscope.sql

Copyright 2000-2007 Steven Feuerstein - Page 99


New compile-time warnings

 PLW-6009: Exception handler does not re-raise an


exception.
 PLW-7205: warning on mixed use of integer types
– Namely, SIMPLE_INTEGER mixed with PLS_INTEGER
and BINARY_INTEGER
 PLW-7206: unnecessary assignments
 Lots of PRAGMA INLINE-related warnings
 More feedback on impact of optimization
– PLW-6007: Notification that entire subprograms were
removed

plw*.sql files
Copyright 2000-2007 Steven Feuerstein - Page 100
Oracle11g PL/SQL: Easier to use, faster to
execute, expanded capabilities
 Function result cache a very elegant solution to static
data querying
 PL/Scope offers wonderful new analytical capabilities
on our code base
 Fewer gaps in functionality
– Dynamic SQL interoperability, CONTINUE, FOLLOWS for
triggers, etc.
 Now all we have to do is convince our management to
upgrade to Oracle11g!

Copyright 2000-2007 Steven Feuerstein - Page 101


Dynamic SQL and dynamic PL/SQL

 An overview of dynamic SQL


 Dynamic PL/SQL block execution
 Method 4 dynamic SQL
 When DBMS_SQL comes in handy...
– Parse very long strings
– Describe columns in dynamic query
– And method 4...
 Oracle11g enhancements

Copyright 2000-2007 Steven Feuerstein - Page 102


What is Dynamic SQL?

 Dynamic SQL actually refers, in the world of


PL/SQL, to two things:
– SQL statements, such as a DELETE or CREATE
TABLE, that are constructed and executed at run-
time.
– Anonymous PL/SQL blocks that are constructed,
compiled and executed at run-time.

'DROP ' || 'BEGIN ' ||


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

Copyright 2000-2007 Steven Feuerstein - Page 103


Some of the possibilities with Dynamic SQL
 Build ad-hoc query and update applications.
– The user decides what to do and see.
 Execute DDL statements from within PL/SQL.
– Not otherwise allowed in a PL/SQL block.
 Soft-code your application logic, placing business
rules in tables and executing them dynamically.
– Usually implemented through dynamic PL/SQL

Copyright 2000-2007 Steven Feuerstein - Page 104


Two Methods Available

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

Copyright 2000-2007 Steven Feuerstein - Page 105


NDS or DBMS_SQL: Which should you
use?
tabcount.sf
tabcount81.sf

 Reasons to go with NDS:  Why You'd Use DBMS_SQL:


– Ease of use – Method 4 Dynamic SQL
– Works with all SQL – DESCRIBE columns of
datatypes (including cursor
user-defined object and – SQL statements larger than
collection types) 32K
– Fetch into records and – Better reuse of parsed SQL
collections of records statements -- persistent
– Usually faster runtime cursor handles!
performance

Bottom line: NDS should be your first choice.


Copyright 2000-2007 Steven Feuerstein - Page 106
Four Dynamic SQL Methods

 Method 1: DDL or DML without bind variables


– EXECUTE IMMEDIATE string
 Method 2: DML with fixed number of bind variables
– EXECUTE IMMEDIATE string USING
 Method 3: Query with fixed number of expressions in
the select list
– EXECUTE IMMEDIATE string INTO
 Method 4: Query with dynamic number of expressions
in select list or DML with dynamic number of bind
variables.
– DBMS_SQL!
Copyright 2000-2007 Steven Feuerstein - Page 107
Method 1: DDL within PL/SQL

 Very easy, very dangerous with NDS.


– Here's a procedure that "drops whatever".
CREATE OR REPLACE PROCEDURE drop_whatever (nm IN VARCHAR2)
AUTHID CURRENT_USER
IS
CURSOR type_cur IS
SELECT object_type FROM USER_OBJECTS
WHERE object_name LIKE UPPER (nm);
type_rec type_cur%ROWTYPE; dropwhatever.sp
BEGIN creind81.sp
OPEN type_cur; FETCH type_cur INTO type_rec; health$.pkg
settrig.sp
IF type_cur%FOUND THEN
EXECUTE IMMEDIATE
'DROP ' || type_rec.object_type || ' ' || nm;
END IF;
END;

Copyright 2000-2007 Steven Feuerstein - Page 108


Method 2: DML (Update) with 2 bind variables

CREATE OR REPLACE PROCEDURE salary_raise (


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

FOR i IN loc_array.FIRST .. loc_array.LAST LOOP


dml_str := 'UPDATE emp_' || loc_array (i)
|| ' SET sal = sal * (1+(:XYZ/100))'
|| ' WHERE job = :123';
EXECUTE IMMEDIATE dml_str USING raise_percent, job;
END LOOP;
END;

Copyright 2000-2007 Steven Feuerstein - Page 109


Method 3: COUNT(*) For Any Table

 Here's
CREATE a handy
OR REPLACE and tabCount
FUNCTION simple utility
( based on NDS:
tab IN VARCHAR2, whr IN VARCHAR2 := NULL, sch IN VARCHAR2 := NULL)
RETURN INTEGER
IS Specify schema, table and
retval INTEGER;
BEGIN
WHERE clause...
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || NVL (sch, USER) ||
'.' || tab || ' WHERE ' || NVL (whr, '1=1') INTO retval;
RETURN retval;
END;

IF tabCount ('citizens', 'insured = ''NO''') > 40,000,000


THEN
DBMS_OUTPUT.PUT_LINE (
'Not the best health care system in the world....');
END IF;
tabcount81.sf
tabcount.sf

Copyright 2000-2007 Steven Feuerstein - Page 110


Quiz!

 What's wrong with PROCEDURE process_lineitem (


line_in IN INTEGER)
this code? IS
BEGIN
 How would you fix IF line_in = 1
THEN
it? process_line1;
END IF;
 
IF line_in = 2
THEN
process_line2;
END IF;
  ...
IF line_in = 22045
THEN
process_line22045;
END IF;
END;

Copyright 2000-2007 Steven Feuerstein - Page 111


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

 Dynamically construct, compile and run an


anonymous block with EXECUTE IMMEDIATE.
– Begins with BEGIN or DECLARE.
– Ends with END;. The trailing semi-colon is required;
otherwise it is parsed as an SQL statement.
 You can only reference globally-accessible data
structures (declared in a package specification).
 Exceptions can (and should) be trapped in the
block from which the dynamic PL/SQL was
executed. dynplsql8i.sp
dynplsql_nolocal.sql

Copyright 2000-2007 Steven Feuerstein - Page 113


Dynamic PL/SQL Possibilities

 There are so many possibilities....some things I have


done:
– Dramatically reduce code volume, improve performance.
– Generic string parsing engine: parse any string into your own
collection.
– Generic calculator engine.
– Implement support for "indirect referencing": read and
change values of variables whose names are only
determined at run-time.
 And there are also dangers: SQL or code injection.
dynvar.pkg
dyncalc.pkg
str2list.*
Copyright 2000-2007 Steven Feuerstein - Page 114 filepath*.pkg
SQL (code) Injection

 "Injection" means that unintended and often


malicious code is inserted into a dynamic
SQL statement.
– Biggest risk occurs with dynamic PL/SQL, but it
is also possible to subvert SQL statements.
 Best ways to avoid injection:
– Restrict privileges tightly on user schemas.
– Use bind variables whenever possible.
– Check dynamic text for dangerous text.
code_injection.sql
sql_guard.*
Copyright 2000-2007 Steven Feuerstein - Page 115
Method 4 Dynamic SQL
with DBMS_SQL and NDS
 Method 4 dynamic SQL is the most generalized
and most complex - by far!
– You don't know at compile time either the number of
columns or the number of bind variables.
– With DBMS_SQL, you must put calls to
DBMS_SQL.DEFINE_COLUMN and/or
DBMS_SQL.BIND_VARIABLE into loops.
 With NDS, you must shift from dynamic SQL to
dynamic PL/SQL.
– How else can you have a variable INTO or USING
clause?

Copyright 2000-2007 Steven Feuerstein - Page 116


Dynamic "SELECT * FROM <table>" in PL/SQL

 You provide the table and WHERE clause. I


display all the data.
– I don't know in advance which or how many rows
to query.
 I can obtain the column information from
ALL_TAB_COLUMNS...and from there the
fun begins!
 You can do it with NDS, but you have to
switch to dynamic PL/SQL and it is quite ugly.
intab.sp
intab9i.sp
intab9i.tst

Copyright 2000-2007 Steven Feuerstein - Page 117


Pseudo-code flow for
DBMS_SQL implementation
BEGIN
Build the FOR each-column-in-table LOOP
SELECT list add-column-to-select-list;
END LOOP;
Parse the
DBMS_SQL.PARSE (cur, select_string, DBMS_SQL.NATIVE);
variable SQL
FOR each-column-in-table LOOP
Define each
DBMS_SQL.DEFINE_COLUMN (cur, nth_col, datatype);
column
END LOOP;

Execute the fdbk := DBMS_SQL.EXECUTE (cur);


query
LOOP
fetch-a-row;
Extract each FOR each-column-in-table LOOP
value DBMS_SQL.COLUMN_VALUE (cur, nth_col, val);
END LOOP;
END LOOP;
Lots of code, but relatively
END; intab.sp
straightforward
Copyright 2000-2007 Steven Feuerstein - Page 118
Parsing very long strings

 One problem with EXECUTE IMMEDIATE is


that you pass it a single VARCHAR2 string.
– Maximum length 32K.
 So what do you do when your string is longer?
– Very likely to happen when you are generating SQL
statements based on tables with many columns.
– Also when you want to dynamically compile a
program.
 Time to switch to DBMS_SQL!

Copyright 2000-2007 Steven Feuerstein - Page 119


DBMS_SQL.PARSE overloading for collections

 Oracle offers an overloading of


DBMS_SQL.PARSE that accepts a collection
of strings, rather than a single string.
 DBMS_SQL offers two different array types:
– DBMS_SQL.VARCHAR2S - each string max 255
bytes.
– DBMS_SQL.VARCHAR2A - each string max
32,767 bytes (new in Oracle9i).

Copyright 2000-2007 Steven Feuerstein - Page 120


Compile DDL from a file with DBMS_SQL
CREATE OR REPLACE PROCEDURE exec_ddl_from_file (
dir_in IN VARCHAR2
, file_in IN VARCHAR2
)
IS
l_file UTL_FILE.file_type;
l_lines DBMS_SQL.varchar2s;
l_cur PLS_INTEGER := DBMS_SQL.open_cursor;

PROCEDURE read_file (lines_out IN OUT DBMS_SQL.varchar2s) IS


BEGIN
... Read each line into array; see compile_from_file.sql
l_file := UTL_FILE.fopen (dir_in, file_in, 'R');
END read_file;
BEGIN
read_file (l_lines);
DBMS_SQL.parse (l_cur
You can specify a , l_lines
subset of lines in , l_lines.FIRST
the array. , l_lines.LAST
, TRUE
, DBMS_SQL.native
);
DBMS_SQL.close_cursor (l_cur); exec_ddl_from_file.sql
Copyright 2000-2007 Steven Feuerstein - Page 121
Describe columns in a query

 DBMS_SQL offers the ability to "ask" a


cursor to describe the columns defined in
that cursor.
 By using the DESCRIBE_COLUMNS
procedure, you can sometimes avoid
complex parsing and analysis logic.
– Particularly useful with method 4 dynamic SQL.

desccols.pkg
desccols.tst

Copyright 2000-2007 Steven Feuerstein - Page 122


11g Dynamic SQL interoperability and completeness

 EXECUTE IMMEDIATE and DBMS_SQL.PARSE


now accept a CLOB.
 Transform a cursor variable into a DBMS_SQL
cursor, and vice versa.
 DBMS_SQL bulk binds now work with your collection
types and not just Oracle's.

 Why? You can use DBMS_SQL when you need to


handle very complex cases (dynamic SQL method 4),
but then switch to cursor variables to make it easier
to move data to program variables.

Copyright 2000-2007 Steven Feuerstein - Page 123


11g Dynamic SQL Interoperability Example
DECLARE
l_query clob := 'SELECT ... ';
l_cursor NUMBER := DBMS_SQL.open_cursor ();
l_curvar sys_refcursor;

TYPE key_data_cur_t IS TABLE OF some_record_type;


Start with
l_key_data key_data_cur_t;
DBMS_SQL, BEGIN
leveraging the DBMS_SQL.parse (c => cur_num
flexibility of its , language_flag => DBMS_SQL.native
, STATEMENT => l_query
low-level API. );
DBMS_SQL.bind_variable (cur_num, ':d', department_id);
DBMS_SQL.bind_variable (cur_num, ':s', salary);
dummy := DBMS_SQL.EXECUTE (cur_num);
Use cursor
variables to l_curvar := DBMS_SQL.to_refcursor (cur_num);
easily transfer
data to the FETCH l_curvar BULK COLLECT INTO l_key_data;
collection CLOSE l_curvar;
END;

Copyright 2000-2007 Steven Feuerstein - Page 124


The Wonder Of Table Functions
 A table function is a function that you can call in the
FROM clause of a query, and have it be treated as if it
were a relational table.
 Table functions allow you to perform arbitrarily
complex transformations of data and then make that
data available through a query.
– Not everything can be done in SQL.
 Combined with REF CURSORs, you can now more
easily transfer data from within PL/SQL to host
environments.
– Java, for example, works very smoothly with cursor
variables

Copyright 2000-2007 Steven Feuerstein - Page 125


Building a table function

 A table function must return a nested table or


varray based on a schema-defined type.
– Types defined in a PL/SQL package can only be
used with pipelined table functions.
 The function header and the way it is called
must be SQL-compatible: all parameters use
SQL types; no named notation.
– In some cases (streaming and pipelined
functions), the IN parameter must be a cursor
variable -- a query result set.

Copyright 2000-2007 Steven Feuerstein - Page 126


Simple table function example

 Return a list of names as a nested table, and


then call that function in the FROM clause.
CREATE OR REPLACE FUNCTION lotsa_names (
base_name_in IN VARCHAR2, count_in IN INTEGER
)
RETURN names_nt
SELECT column_value
IS
FROM TABLE (
retval names_nt := names_nt ();
lotsa_names ('Steven'
BEGIN
, 100)) names;
retval.EXTEND (count_in);
COLUMN_VALUE
FOR indx IN 1 .. count_in
LOOP
retval (indx) :=
------------
base_name_in || ' ' || indx;
Steven 1
END LOOP;

RETURN retval;
... tabfunc_scalar.sql
END lotsa_names;

Copyright 2000-2007 Steven Feuerstein - Page 127


Steven 100
Streaming data with table functions

 You can use table functions to "stream" data through several stages
within a single SQL statement.
– Example: transform one row in the stocktable to two rows in the tickertable.

CREATE TABLE stocktable (


ticker VARCHAR2(20),
trade_date DATE,
open_price NUMBER,
close_price NUMBER
)
/
CREATE TABLE tickertable (
ticker VARCHAR2(20),
tabfunc_streaming.sql
pricedate DATE,
pricetype VARCHAR2(1),
price NUMBER)
Copyright 2000-2007 Steven Feuerstein - Page 128 /
Streaming data with table functions - 2
 In this example, transform each row of the
stocktable into two rows in the tickertable.
CREATE OR REPLACE PACKAGE refcur_pkg
IS
TYPE refcur_t IS REF CURSOR
RETURN stocktable%ROWTYPE;
END refcur_pkg;
/

CREATE OR REPLACE FUNCTION stockpivot (dataset refcur_pkg.refcur_t)


RETURN tickertypeset ...

BEGIN
INSERT INTO tickertable
SELECT *
FROM TABLE (stockpivot (CURSOR (SELECT *
FROM stocktable)));
END;
/

Copyright 2000-2007 Steven Feuerstein - Page 129 tabfunc_streaming.sql


Use pipelined functions to enhance
performance.
CREATE FUNCTION StockPivot (p refcur_pkg.refcur_t)
RETURN TickerTypeSet PIPELINED

 Pipelined functions allow you to return data


iteratively, asynchronous to termination of the
function.
– As data is produced within the function, it is passed back
to the calling process/query.
 Pipelined functions can only be called within a SQL
statement.
– They make no sense within non-multi-threaded PL/SQL
blocks.

Copyright 2000-2007 Steven Feuerstein - Page 130


Applications for pipelined functions

 Execution functions in parallel.


– In Oracle9i Database Release 2 and above, use the
PARALLEL_ENABLE clause to allow your pipelined
function to participate fully in a parallelized query.
– Critical in data warehouse applications.
 Improve speed of delivery of data to web
pages.
– Use a pipelined function to "serve up" data to the
webpage and allow users to being viewing and
browsing, even before the function has finished
retrieving all of the data.
Copyright 2000-2007 Steven Feuerstein - Page 131
Piping rows out from a pipelined function
CREATE FUNCTION stockpivot (p refcur_pkg.refcur_t)
RETURN tickertypeset
Add PIPELINED
PIPELINED
keyword to header
IS
out_rec tickertype :=
tickertype (NULL, NULL, NULL);
in_rec p%ROWTYPE;
BEGIN
LOOP
FETCH p INTO in_rec;
EXIT WHEN p%NOTFOUND;
out_rec.ticker := in_rec.ticker;
Pipe a row of data out_rec.pricetype := 'O';
back to calling block out_rec.price := in_rec.openprice;
or query
PIPE ROW (out_rec);
END LOOP;
CLOSE p;
RETURN...nothing at
all! RETURN;
END;

tabfunc_setup.sql
Copyright 2000-2007 Steven Feuerstein - Page 132 tabfunc_pipelined.sql
Enabling Parallel Execution
 You can use pipelined functions with the Parallel Query
option to avoid serialization of table function execution.
 Include the PARALLEL_ENABLE hint in the program
header.
– Choose a partition option that specifies how the function's
execution should be partitioned.
– "ANY" means that the results are independent of the order in
which the function receives the input rows (through the REF
CURSOR).

{[ORDER | CLUSTER] BY column_list}


PARALLEL_ENABLE ({PARTITION p BY
[ANY | (HASH | RANGE) column_list]} )

Copyright 2000-2007 Steven Feuerstein - Page 133


Table functions - Summary

 Table functions offer significant new flexibility


for PL/SQL developers.
 Consider using them when you...
– Need to pass back complex result sets of data
through the SQL layer (a query);
– Want to call a user defined function inside a
query and execute it as part of a parallel query.

Copyright 2000-2007 Steven Feuerstein - Page 134


>> Object-Oriented PL/SQL

Object Types and


Object-Oriented
Development
in PL/SQL

Copyright 2000-2007 Steven Feuerstein - Page 135


Object Types in Oracle
 An object type is similar to a package, but is Oracle's
version of an O-O class.
– Object type specification and body
 Few development shops work with object types.
– The initial implementation was weak, but it has gotten much
stronger (with support for inheritance, in particular).
 Oracle uses object types in many of its new features
(e.g., Oracle AQ, the XML datatype).
– This has led more and more Oracle shops to take advantage
of this functionality.

Copyright 2000-2007 Steven Feuerstein - Page 136


A Very Simple Object Type Example

CREATE TYPE food_t AS OBJECT (


name VARCHAR2(100),
food_group VARCHAR2 (100), Attributes
grown_in VARCHAR2 (100)
);

 The food type contains three attributes and no


methods or programs.
 Very similar to a CREATE TABLE statement
– Big difference: an object type is not a "container"
for data.
– Instead, it is a "template" for instances of that type.
Copyright 2000-2007 Steven Feuerstein - Page 137
Working with Simple Objects
DECLARE
my_favorite_vegetable food_t
Create a new := food_t ('Brussel Sprouts',
object with a 'VEGETABLE',
constructor. 'Farm,Greenhouse,Backyard');
BEGIN
DBMS_OUTPUT.put_line (
Read an attribute my_favorite_vegetable.name);
value
my_favorite_vegetable.food_group :=
'SATISFACTION';
Modify an
IF INSTR (
attribute value my_favorite_vegetable.grown_in,
'yard') > 0
THEN
Pass an object order_seeds (my_favorite_vegetable);
as a parameter END IF;
END;

objtype.sql
Copyright 2000-2007 Steven Feuerstein - Page 138
"Real" object types have methods

 Constructor
– A function that returns an instance of this object type, used to
initialize that instance before use.
 Static
– A subprogram applied to the object type as a whole, not any
particular instance
 Member
– A subprogram applied to an instance of an object type
 Use SELF to refer to the current instance.
– Not allowed in static methods.
 You can call packaged code from within an object body.

Copyright 2000-2007 Steven Feuerstein - Page 139


An Object Type with Methods

 The timer CREATE TYPE tmr_t AS OBJECT (


starttime INTEGER
, endtime INTEGER
object , startcputime
, endcputime
INTEGER
INTEGER Attributes
calculates , repetitions
, NAME
INTEGER
VARCHAR2 (2000)

elapsed time. , MEMBER PROCEDURE go


, MEMBER PROCEDURE STOP (text IN VARCHAR2)
 It consists of ,
,
MEMBER FUNCTION timing RETURN INTEGER
MEMBER FUNCTION cputiming RETURN INTEGER

six attributes , MEMBER FUNCTION timing_desc


RETURN VARCHAR2
, STATIC FUNCTION make (name_in IN VARCHAR2) Methods
and all three , CONSTRUCTOR FUNCTION tmr_t (
SELF IN OUT tmr_t
types of , NAME IN VARCHAR2
, repetitions IN INTEGER
methods. )
RETURN SELF AS RESULT
);

tmr.ot
Copyright 2000-2007 Steven Feuerstein - Page 140 thisuser.*
More on Constructor Functions
CREATE TYPE tmr_t AS OBJECT (
 Always pass SELF ...
CONSTRUCTOR FUNCTION tmr_t (
as an IN OUT SELF IN OUT tmr_t,
name IN VARCHAR2,
argument. repetitions IN INTEGER
)
– SELF is assumed for );
RETURN SELF AS RESULT

member methods. CREATE OR REPLACE TYPE BODY tmr_t


 Return SELF as AS
CONSTRUCTOR FUNCTION tmr_t (

well. SELF IN OUT tmr_t,


name IN VARCHAR2,
repetitions IN INTEGER
 Populate SELF )
RETURN SELF AS RESULT
inside the IS
BEGIN
constructor. SELf.repetitions := repetitions;
SELF.name := name;
RETURN;
END;
END;
Copyright 2000-2007 Steven Feuerstein - Page 141
Support for inheritance in object types

 You can now define a hierarchy of subtypes of


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

Copyright 2000-2007 Steven Feuerstein - Page 142


Let's Build a Type Hierarchy

"root",  We have a three level


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

food.ot

Copyright 2000-2007 Steven Feuerstein - Page 143


Creating a Simple Object Type Hierarchy

CREATE TYPE food_t AS OBJECT (


name VARCHAR2(100),  NOT FINAL
food_group VARCHAR2 (100),
grown_in VARCHAR2 (100)) indicates that this
NOT FINAL;
type can be a
CREATE TYPE dessert_t UNDER food_t ( supertype.
contains_chocolate CHAR(1),
year_created NUMBER(4))  UNDER denotes
NOT FINAL;
that this type is a
CREATE TYPE cake_t UNDER dessert_t (
diameter NUMBER, subtype.
inscription VARCHAR2(200));
food.ot

An object instantiated from food_t has three attributes. A dessert


object has five attributes. A cake has seven.
Copyright 2000-2007 Steven Feuerstein - Page 144
Substitutability of Object Types

"Any object of type cake is also a


dessert, is also a food."
 A supertype is substitutable if one of its
subtypes can substitute or stand in for it in
a slot (a variable, column, etc.) whose
declared type is the supertype.
 Oracle supports object type substitution in
columns of relational tables, attributes of
object types and elements in collections.
Copyright 2000-2007 Steven Feuerstein - Page 145
Populate an Object Table
 Create a table of objects of type food (root type).
CREATE TABLE sustenance OF food_t;

 Populate it with objects at different levels in hierarchy.


DECLARE Use of constructor to
my_favorite_vegetables food_t := initialize a variable
food_t ('Brussel Sprouts', 'VEGETABLE', 'farm' );
BEGIN
INSERT INTO sustenance VALUES (my_favorite_vegetables);

INSERT INTO sustenance


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

INSERT INTO sustenance Substitution of subtypes


VALUES (cake_t (
'Marzepan Delight', 'CARBOHYDRATE', 'bakery',
'N', 1634, 8, 'Happy Birthday!' ) ); food.ot
END;
Copyright 2000-2007 Steven Feuerstein - Page 146
Objects in a Collection

 Create a table of objects of type food - root type.

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

Copyright 2000-2007 Steven Feuerstein - Page 147


Accessing Attributes in Substituted Types
 You can substitute a subtype in a supertype column or attribute,
but subtype-specific attributes and methods are by default not
visible.

SQL> DECLARE
4 mmm_good food_t :=
5 dessert_t ('Super Brownie', 'CARBOHYDRATE',
6 'my oven', 'Y', 1994);
7 BEGIN
8 DBMS_OUTPUT.PUT_LINE (mmm_good.contains_chocolate);
9 END;
10 /
DBMS_OUTPUT.PUT_LINE (mmm_good.contains_chocolate);
*
ERROR at line 8:
PLS-00302: component 'CONTAINS_CHOCOLATE' must be declared

Copyright 2000-2007 Steven Feuerstein - Page 148


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

/* Will fail, since main_course is of food_t type */


SELECT main_course.contains_chocolate
FROM meal
WHERE TREAT (main_course AS dessert_t) IS NOT NULL;

/* Now works, since I am treating main_course as a dessert */


SELECT TREAT (main_course AS dessert_t).contains_chocolate
FROM meal
WHERE TREAT (main_course AS dessert_t) IS NOT NULL;

/* Set to NULL any desserts that are not cakes... */


UPDATE meal treat.sql
SET dessert = TREAT (dessert AS cake_t);

Copyright 2000-2007 Steven Feuerstein - Page 149


Turning Off Substitutability
 Oracle provides syntax to turn off substitutability on either
an entire type or specific attributes of a type.

CREATE TABLE brunches OF food_t


NOT SUBSTITUTABLE AT ALL LEVELS;
At the table level
CREATE TABLE meal (
served_on DATE,
appetizer food_t,
main_course food_t, For a single column
dessert dessert_t
)
COLUMN appetizer NOT SUBSTITUTABLE AT ALL LEVELS;

Copyright 2000-2007 Steven Feuerstein - Page 150


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

Copyright 2000-2007 Steven Feuerstein - Page 151


Constraining Substitutability to a Specific Type

 Suppose I want a column of desserts to


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

You can only constrain to a notsubst.sql


single type, not a list of types. isof.sql

Copyright 2000-2007 Steven Feuerstein - Page 152


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

 Some object types should only be used as supertypes for


other types; they are so general, you would not actually
create and manipulate instances from those types.
– Oracle allows you to define object types as NOT
INSTANTIABLE, as shown above.

food2.ot
Copyright 2000-2007 Steven Feuerstein - Page 153
Creating and Overriding Methods
 Most real-world object types will have both
attributes and methods, programs that
perform operations on attributes.
 With inheritance, you can:
– inherit supertype methods
– override or replace supertype methods with
subtype implementations
– add completely new methods

Copyright 2000-2007 Steven Feuerstein - Page 154


Overriding to Provide Specificity for
Subtypes
CREATE OR REPLACE TYPE BODY dessert_t
IS
 Different OVERRIDING MEMBER
FUNCTION price RETURN NUMBER IS

calculations for mult NUMBER := 1;


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

food2.ot
Copyright 2000-2007 Steven Feuerstein - Page 155
Disallowing Overrides

CREATE TYPE Person_t AS


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

 If you do not want a subtype to modify the behavior


of a supertype method, declare it to be FINAL.
– You can do this even in object types that are NOT FINAL.

Copyright 2000-2007 Steven Feuerstein - Page 156


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

 Suppose the implementation of a method changes with each


subtype in the hierarchy and the supertype's implementation
really is irrelevant.
 Declare the method to be NOT INSTANTIABLE and then (a)
you do not have to provide an implementation of the method
and (b) all subtypes must provide an implementation.
food2.ot

Copyright 2000-2007 Steven Feuerstein - Page 157


11g
Oracle 11g: Referencing supertype methods
 New to Oracle 11g, you can invoke a supertype's
method in your override of that method.
– Useful when you want to "add on" to supertype method,
but you certainly don't want to have to copy/paste the
code needed.
 One very typical example is when you want to
"display" an object.
– Show values of attributes of each type in the hierarchy.
– Each "level" has its own "to _string" function.

11g_gen_invoc.sql

Copyright 2000-2007 Steven Feuerstein - Page 158


And then there is Dynamic Polymorphism

 POLY-MORPHISM: multiple forms.


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

Copyright 2000-2007 Steven Feuerstein - Page 159


Exploring Dynamic Polymorphism

 The food and CREATE TYPE food_t AS OBJECT (


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

Copyright 2000-2007 Steven Feuerstein - Page 160


A Visual Representation
 The root price
food Price
function is over-
ridden in the
the "original"
dessert subtype.
 The cake subtype
dessert Price
now simply
inherits its price
An override calculation from
its dessert
cake supertype.
Inherited
calculation
Copyright 2000-2007 Steven Feuerstein - Page 161
Dynamically Choosing the Right Method
DECLARE
TYPE foodstuffs_nt IS TABLE OF food_t;
A collection of foods is
fridge foodstuffs_nt populated with three
:= foodstuffs_nt ( different object types.
food_t ('Eggs benedict', ...),
dessert_t ('Strawberries and cream', ...),
cake_t ('Chocolate Supreme', ...));
BEGIN
FOR indx IN
fridge.FIRST .. food3.ot
fridge.LAST food4.ot
LOOP food5.ot
DBMS_OUTPUT.put_line ( dynpoly_overhead.tst
'Price of ' || fridge (indx).NAME ||
' = ' ||
fridge (indx).price); The price invocation is
END LOOP; resolved at run-time, and
END; not necessarily as the
food_t.price method.
Copyright 2000-2007 Steven Feuerstein - Page 162
Other Object-Relational topics

 Object tables
 The REF function and UTL_REF
 Object comparisons
– MAP and ORDER
 Object views
– INSTEAD OF triggers

Copyright 2000-2007 Steven Feuerstein - Page 163


Object tables
 You can create a table of objects, known as an
object table.
– Use special OF syntax to specify it.
CREATE TABLE food_table OF food_t

 Specify a primary key (from object attributes) or


Oracle will create an object identifier, OID.
– When the type does not have subtypes, you can use
conventional DML to change values of attributes.
 Use the VALUE function to retrieve an object from the
table. object_table.sql
SELECT VALUE (f).name
FROM food_table
Copyright 2000-2007 Steven Feuerstein - Page 164
The REF function

 A REF is a logical pointer to a row in an object


table.
– The value itself of little interest: a long hex string.
– The REF contains: the primary key or OID value; a
unique designator for the table; optionally, a ROWID.
 You can use a REF to retrieve a row object without
having to name the table!
– That is, avoid the need for an explicit join to a table.
 But no one is going to make you do this.

object_ref.sql

Copyright 2000-2007 Steven Feuerstein - Page 165


The UTL_REF package
 UTL_REF allows you to manipulate objects through
its REF directly in PL/SQL.
– You can lock, select, update or delete an object given only
its REF.
CREATE OR REPLACE TYPE BODY subject_t
AS
MEMBER FUNCTION print_bt (str IN VARCHAR2)
RETURN VARCHAR2
IS
bt subject_t;
BEGIN
IF SELF.broader_term_ref IS NULL
THEN
RETURN str;
ELSE
UTL_REF.select_object (SELF.broader_term_ref, bt);
RETURN bt.print_bt (
NVL (str, SELF.NAME)) || ' (' || bt.NAME || ')';
END IF;
END;
END; object_ref.sql
Copyright 2000-2007 Steven Feuerstein - Page 166
UTL_REF programs

 UTL_REF.SELECT_OBJECT
– Retrieves a copy of the object specified by the REF.
 UTL_REF.LOCK_OBJECT
– Locks and optionally retrieves a copy of the specified
object.
 UTL_REF.UPDATE_OBJECT
– Replaces the object specified by the REF with the object
in the parameter list.
 UTL_REF.DELETE_OBJECT
– Deletes the object pointed to by the REF.

Copyright 2000-2007 Steven Feuerstein - Page 167


Comparing two objects

 When objects are columns, you immediately


run into the issue of how to compare them,
sort them.
 How is Oracle supposed to make sense of
such statements as:
SELECT * FROM meals
ORDER BY main_course;

DECLARE
l_dinner food_t;
l_dessert dessert_t;
BEGIN
IF l_dinner > l_dessert THEN
...

Copyright 2000-2007 Steven Feuerstein - Page 168


Four options for comparisons

 Attribute-level comparison
– Check explicitly in your code based on attribute values.
 Default SQL
– Simple equality test of each attribute, only works with scalars-
only object types.
– Not too useful; primary key values will never match.
 MAP member method
– Map an object 'value" to a datatype that Oracle knows how to
compare.
 ORDER member method
– Compares two objects and returns a flag indicating their relative
order.

Copyright 2000-2007 Steven Feuerstein - Page 169


The MAP member method
 Suppose I want food to be ordered as follows:
– "First" - vegetables
– "Last" - protein
– "In between" - carbohydrates, fluids, etc.
– Within a food group, a "tie" is broken based on the number of
characters in the name. Silly, I know.
CREATE OR REPLACE TYPE BODY food_t IS
MAP MEMBER FUNCTION food_mapping RETURN NUMBER IS
BEGIN
RETURN ( CASE SELF.food_group
WHEN 'PROTEIN' THEN 30000
WHEN 'LIQUID' THEN 20000
WHEN 'CARBOHYDRATE' THEN 15000
WHEN 'VEGETABLE' THEN 10000
END map_example.sql
+ LENGTH (SELF.NAME));
END;
END;
Copyright 2000-2007 Steven Feuerstein - Page 170
The ORDER member method
 An ORDER method accepts an object and
compares it to SELF. It must return values as
shown below:
For these semantics... ORDER must return...
SELF < argumentObject Any negative number, usually -1
SELF = argumentObject 0
SELF > argumentObject Any positive number, usually 1
Undefined comparison NULL

 So...you only need to write the appropriate code


for your object type.
order_example.sql
Copyright 2000-2007 Steven Feuerstein - Page 171
Object Views

 An object view is a view "OF" object types,


but whose defining query gathers data from
relational tables.
 If you have an existing relational design, but
want to exploit O-O features on this data,
create an object view on top of the relational
data.
 Will need to specify the object and the WITH
OBJECT IDENTIFIER clause.
object_view.sql
Copyright 2000-2007 Steven Feuerstein - Page 172
INSTEAD OF triggers

 You cannot perform DML operations directly


against the underlying relational tables through
the object view.
 Instead, create INSTEAD OF triggers that
"intercept" the DML operation and transform it
into something sensible.
CREATE OR REPLACE TRIGGER images_v_insert
INSTEAD OF INSERT ON images_v
FOR EACH ROW
BEGIN
/* Call a packaged procedure to perform the insert. */
manage_image.create_one (
:NEW.image_id, :NEW.file_type,
:NEW.file_name, :NEW.bytes, :NEW.keywords);
END;
Copyright 2000-2007 Steven Feuerstein - Page 173
Helpful data dictionary views
 USER_TYPES
– All of the types in my schema, from object type to collection type.
 USER_TYPE_ATTR
– All of the attributes in my object types (similar to
USER_TAB_COLUMNS)
 USER_TYPE_METHODS
– All of the methods defined in my object types (similar to
USER_PROCEDURES)
 USER_OBJECT_TABLES
– List of all object tables defined on specific relational tables
– Note: object tables do not appear in USER_OBJECTS. Instead, you
must query from USER_ALL_TABLES
 USER_METHOD_RESULTS
– Gives you the datatype returned by methods in types

Copyright 2000-2007 Steven Feuerstein - Page 174


Object Types Summary

 They are finally becoming robust enough to


be useful.
 Object types are being used extensively by
Oracle itself.
– Advanced Queuing and XMLtype, among others.
– This fact makes me more confident of the future,
performance, and capabilities of object types.
 Get familiar with the syntax so that you can
work with object types with confidence.
Copyright 2000-2007 Steven Feuerstein - Page 175
Built-in package enhancements of note

 UTL_FILE
– Not nearly as limited as before!
 DBMS_OUTPUT.PUT_LINE
– Big strings! Big buffer! (Oracle10g Release 2)
 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
– Finally, the missing link has been added.
 DBMS_RANDOM
– Straightforward random number generation
 UTL_RECOMP
– Powerful recompile capability
 UTL_MAIL
– Send emails with ease

Copyright 2000-2007 Steven Feuerstein - Page 176


>> File IO in PL/SQL

 UTL_FILE allows you to read from and write


to operating system files on the database
server.
 Prior to Oracle9iR2, it was a fairly primitive
utility, but Oracle9iR2 offers many
enhancements.
– Remove, copy, rename files. Work with
database directories.

Copyright 2000-2007 Steven Feuerstein - Page 177


Authorizing Directory Access

 With Oracle9iR2, you can read/write files in directories specified by...


– The UTL_FILE_DIR parameter or...
– Database directory objects
 Oracle requires you to list explicitly those directories you wish to be able
to read/write with UTL_FILE.
– You do this by adding lines to the instance parameter file.

utl_file_dir = /tmp
utl_file_dir = /accounts/newdev

Copyright 2000-2007 Steven Feuerstein - Page 178


Working with Database Directories

 A Directory is a database "object", define with


a CREATE statement.
– You need CREATE ANY DIRECTORY privilege.
CREATE OR REPLACE DIRECTORY ERROR_LOG AS '/tmp/apps/log';

SELECT owner, directory_name, directory_path


View those directories FROM ALL_DIRECTORIES;
to which you have
access.

GRANT READ ON DIRECTORY error_log TO SCOTT;


Grant READ or WRITE
privileges on a
directory.
utlfile_92.sql
Copyright 2000-2007 Steven Feuerstein - Page 179
Opening a File
DECLARE
fid UTL_FILE.FILE_TYPE;
BEGIN
fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'W',
max_linesize => 32767);
END;

 Specify file location, name and operation type.


– Types are 'R' for Read, 'W' for Write and 'A' for Append.
– Specify maximum linesize (Default of 1024, maximum of 32767)
 The FOPEN function returns a record ("file handle") based on
the UTL_FILE.FILE_TYPE.
– Contains three fields (ID, datatype and byte_mode).
 Test to see if file is open with the IS_OPEN function.
– In actuality, this function simply returns TRUE if the file handle's id
field is NOT NULL. Not much of a test...
Copyright 2000-2007 Steven Feuerstein - Page 180
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.
 Maximum length for lines to read/write files is 32K, but
you need to specify that length explicitly.
 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 statusexec_ddl_from_file.sql
flag.
Copyright 2000-2007 Steven Feuerstein - Page 181 getnext.sp infile.sf
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;

 You can use PUT, PUT_LINE or PUTF.


– PUTF is like the C printf program, allowing for some
formatting.
 Call FFLUSH to make sure that everything you have
written to the buffer is flushed out to the file.
create_file.sp
genaa.sql

Copyright 2000-2007 Steven Feuerstein - Page 182


Closing 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);
EXCEPTION
WHEN UTL_FILE.READ_ERROR
THEN
UTL_FILE.FCLOSE (fid);
END;

 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.

Copyright 2000-2007 Steven Feuerstein - Page 183


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

 You can specify an OS directory or a database object of type


DIRECTORY (as shown above)

Copyright 2000-2007 Steven Feuerstein - Page 184


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

 If no error is raised, then you deleted successfully

fremove.sql
fileIO92.pkg

Copyright 2000-2007 Steven Feuerstein - Page 185


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

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

Copyright 2000-2007 Steven Feuerstein - Page 187


Encapsulate and Improve
 The best way to take advantage of the new UTL_FILE features is
to encapsulate or wrap them inside a layer of enhancing code.
– Improve the error handling and reporting.
– Hide the messy details of workarounds/patches. fileIO92.pkg
– Provide easy backups of files. fileIO92.tst

 Still lots of restrictions in UTL_FILE....


– No higher-level file operations supported (change privileges,
create directory, get list of files in directory, random access to
contents).
– Limitations on files you can access (no mapped files, no use
of environmental variables).
– But don't worry....you can always use Java for that!

JFile.java
Copyright 2000-2007 Steven Feuerstein - Page 188 xfile.pkg
DBMS_UTILITY.FORMAT_ERROR_BACKTR
ACE
Long-standing challenge in PL/SQL:

How can I find the line number on which an


error was raised in PL/SQL?

 Before Oracle10g, the only way is to let the error go


unhandled in your PL/SQL code!
 DBMS_UTILITY.FORMAT_ERROR_STACK only
gives you the full error message.
– And is recommended by Oracle in place of SQLERRM.

But in Oracle10g, we have "back trace"!

Copyright 2000-2007 Steven Feuerstein - Page 189


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

Backtrace.sql

Copyright 2000-2007 Steven Feuerstein - Page 190


Displaying the “error stack” inside PL/SQL

CREATE OR REPLACE PROCEDURE proc3


IS
BEGIN
DBMS_OUTPUT.put_line ('calling proc2');
proc2;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line (
DBMS_UTILITY.FORMAT_ERROR_STACK);
END;
/

SQL> exec proc3


calling proc2
calling proc1
running proc1
backtrace.sql
ORA-01403: no data found

Copyright 2000-2007 Steven Feuerstein - Page 191


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

SQL> exec proc3


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

backtrace.sql
Copyright 2000-2007 Steven Feuerstein - Page 192 bt.pkg
DBMS_OUTPUT: relief in sight!

 Oracle10g Release 2 offers some long-


awaited enhancements...
– DBMS_OUTPUT.PUT_LINE will now
accept and display strings up to 32K in 32K!
length.
– You can set the buffer size to UNLIMITED.

SET SERVEROUTPUT ON SIZE UNLIMITED

Copyright 2000-2007 Steven Feuerstein - Page 193


DBMS_RANDOM

 Oracle makes it easy to generate random values.


Two packages....
– DBMS_RANDOM: good for many basic requirements
– DBMS_CRYPTO_TOOLKIT: a more robust and complex
package for high-end security applications
 DBMS_RANDOM subprograms
– VALUE - return a number, either between 0 and 1, or
between specified low and high values
– STRING - return a string of specified type and length

randomizer.*
pick_winners_randomly.*

Copyright 2000-2007 Steven Feuerstein - Page 194


Oracle10g UTL_RECOMP

 The UTL_RECOMP built-in package offers


two programs that you can use to recompile
any invalid objects in your schema:
RECOMP_SERIAL and RECOMP_PARALLEL.
– Must connect as SYSDBA account to use
UTL_RECOMP.
– Parallel version uses DBMS_JOB and will temporarily
disable all other jobs in the queue to avoid conflicts with
the recompilation.
CALL utl_recomp.recomp_serial ('SCOTT');
CALL utl_recomp.recomp_parallel ('SCOTT', 4); recompile.sql

Copyright 2000-2007 Steven Feuerstein - Page 195


Oracle10g UTL_MAIL

 UTL_MAIL makes it much easier to send


email from within PL/SQL by hiding some of
the complexities of UTL_SMTP.
 To use UTL_MAIL...
– Set the SMTP_OUTPUT_SERVER parameter.
– Install the utlmail.sql and prvtmail.plb files under
SYS. That's right - it is not installed by default.
– Grant EXECUTE on UTL_MAIL as desired.

Copyright 2000-2007 Steven Feuerstein - Page 196


Send an email message from PL/SQL

 The interface to the SEND program mimics


the basic "send email" form of Outlook and
other email programs.
BEGIN /* Requires Oracle10g */
UTL_MAIL.send (
sender => 'me@mydomain.com'
,recipients => 'you@yourdomain.com, him@hisdomain.com'
,cc => 'mom@momdomain.com'
,bcc => 'me@mydomain.com'
,subject => 'Cool new API for sending email'
,message =>
'Hi Ya''ll,
Sending email in PL/SQL is *much* easier with UTL_MAIL in 10g.
Give it a try!
Mailfully Yours,
Bill'
);
END;

Copyright 2000-2007 Steven Feuerstein - Page 197


Attachments and UTL_MAIL

 You can attach RAW or VARCHAR2 content


as an attachment (up to 32K).
BEGIN
UTL_MAIL.send_attachment_raw (
sender => 'me@mydomain.com'
,recipients => 'you@yourdomain.com, him@hisdomain.com'
,cc => 'mom@momdomain.com'
,bcc => 'me@mydomain.com'
,subject => 'Cool new API for sending email'
,message => '...'
,attachment => '...' /* Content of the attachment */
,att_inline => TRUE /* Attachment in-line? */
,att_filename => '...'
/* Name of file to hold the attachment after the mail
is received. */
);
END;

Copyright 2000-2007 Steven Feuerstein - Page 198


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

Copyright 2000-2007 Steven Feuerstein - Page 199


A guide to my mentors/resources

 A Timeless Way of Building – a beautiful and deeply spiritual book on


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

Copyright 2000-2007 Steven Feuerstein - Page 200


Some Free PL/SQL Resources

 Oracle Technology Network PL/SQL page


http://www.oracle.com/technology/tech/pl_sql/index.html
 OTN Best Practice PL/SQL
http://www.oracle.com/technology/pub/columns/plsql/index.html
 Oracle documentation – complete, online, searchable!
http://tahiti.oracle.com/
 PL/SQL Obsession - my on-line portal for PL/SQL developers
http://www.ToadWorld.com/SF
 Quest Pipelines
http://quest-pipelines.com/
 I Love PL/SQL and...help improve the PL/SQL language!
http://ILovePLSQLAnd.net
 PL/Vision

http://quest-pipelines.com/pipelines/dba/PLVision/plvision.htm

Copyright 2000-2007 Steven Feuerstein - Page 201


So Much to Learn...

 Don't panic -- but don't stick your head in the


sand, either.
– You won't thrive as an Oracle7, Oracle8 or Oracle8i
developer!
 You can do so much more from within PL/SQL
than you ever could before.
– Familiarity with new features will greatly ease the
challenges you face.
Copyright 2000-2007 Steven Feuerstein - Page 202

You might also like