21st century plsql with 11g
DESCRIPTION
*Brought to you by WebandDB.comTRANSCRIPT
Copyright 2000-2007 Steven Feuerstein - Page 1
21st Century PL/SQL
Blast off to thefaster, smarter, easier world
of Oracle PL/SQLin Oracle10g and Oracle11g
Steven FeuersteinPL/SQL Evangelist, Quest Software
www.ToadWorld.com/SF
Copyright 2000-2007 Steven Feuerstein - Page 2
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 3
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 4
So...why listen to me?Because I am a programmer obsessed...
And I build production applications....
Copyright 2000-2007 Steven Feuerstein - Page 5
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 6
How to benefit most from this session
Watch, listen, focus on concepts and principles. Download and use any of my the training materials:
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!
filename_from_demo_zip.sql
Download and use any of my scripts (examples, performance scripts, reusable code) from the same location: the demo.zip file.
http://www.ToadWorld.com/SFPL/SQL Obsession
Copyright 2000-2007 Steven Feuerstein - Page 7
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 8
The STANDARD Package and the Rdbms/Admin Directory
Much of what we consider to be the base PL/SQL language is defined in the STANDARD package.– One of two "default" packages; the other is
DBMS_STANDARD.
You can view the contents of the STANDARD package (and other "supplied" packages by visiting the appropriate variation of:
$ORACLE_HOME/Rdbms/Admin
standard_demo.sql
Copyright 2000-2007 Steven Feuerstein - Page 9
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 10
System Global Area (SGA) of RDBMS Instance
PL/SQL in Shared Memory
Shared Pool
Large Pool
Reserved Pool
show_empscalc_totals upd_salaries
Select * from emp
Shared SQL
Pre-parsedUpdate emp Set sal=...
Library cache
Session 1 memory (PGA/UGA)
emp_rec emp%rowtype;tot_tab tottabtype;
Session 2 memory (PGA/UGA)
emp_rec emp%rowtype;tot_tab tottabtype;Session 1
Session 2mysess.pkg
sess2.sqlshow_memory.sp
Copyright 2000-2007 Steven Feuerstein - Page 11
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 12
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 13
About Definer Rights
Allows you to centralize access to and control of underlying data structures.
Ignores roles and relies on directly-granted privileges.
But it can be a source of confusion and architectural problems.
Orders
OE Data
OE CodeOrder_Mgt
Cancel
Sam_Sales
PlaceClose Old
Orders
XCannot alter
table directly.
Note: Oracle built-in packages have long had the capability of running
under the invoker's authority.
Copyright 2000-2007 Steven Feuerstein - Page 14
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 15
For top level modules:
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.
Invoker Rights Syntax
CREATE [ OR REPLACE ] <module type> [ AUTHID { DEFINER | CURRENT_USER } ]AS ...
Copyright 2000-2007 Steven Feuerstein - Page 16
"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.
User/Data schemaUser/Data schema
accounts table
PROCEDURE mng_account ISBEGIN ... code.acct_mgr.destroy(...);END;
Central Code schemaCentral Code schema
PACKAGE acct_mgr
...FROM accounts WHERE...
destroy
modify
make
AUTHID
CURRENT_USER
Copyright 2000-2007 Steven Feuerstein - Page 17
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 18
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 19
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.
Compiling with "Template" Objects
Copyright 2000-2007 Steven Feuerstein - Page 20
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.sqlinvdefinv.tstirdynsql.sql
invoker_rights_mode.sf
Copyright 2000-2007 Steven Feuerstein - Page 21
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 22
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 23
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 24
Logging with ATs
logger.splog81.pkglog81*.tst
CREATE OR REPLACE PACKAGE BODY log IS PROCEDURE putline ( code_in IN INTEGER, text_in IN VARCHAR2 ) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO logtab VALUES (code_in, text_in, SYSDATE, USER, SYSDATE, USER, rec.machine, rec.program ); COMMIT;
EXCEPTION WHEN OTHERS THEN ROLLBACK; END;END;
retry.pkgretry.tst
Save on successful exit
Avoid inter-dependencies with
the main transaction.
Don't forget to rollback on error!
Copyright 2000-2007 Steven Feuerstein - Page 25
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 26
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.sqlauton_in_sql.sqlautontrigger*.sql
Copyright 2000-2007 Steven Feuerstein - Page 27
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 28
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 29
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 30
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
Oracle10g
Copyright 2000-2007 Steven Feuerstein - Page 31
Learn about the PL/SQL optimizer
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.
http://www.oracle.com/technology/tech/pl_sql/htdocs/new_in_10gr1.htm
Copyright 2000-2007 Steven Feuerstein - Page 32
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:
and then:
ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL = 1;
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;
Copyright 2000-2007 Steven Feuerstein - Page 33
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';
Oracle10g
Copyright 2000-2007 Steven Feuerstein - Page 34
Compiler time warnings - example
Check for “unreachable end” code….SQL> CREATE OR REPLACE PROCEDURE unreachable_code IS2 x NUMBER := 10;3 BEGIN4 IF x = 10 THEN5 x := 20;6 ELSE7 x := 100; -- unreachable code8 END IF;9 END unreachable_code;10 / SP2-0804: Procedure created with compilation warnings SQL> show errErrors for PROCEDURE UNREACHABLE_CODE: LINE/COL ERROR-------- -------------------------------------7/7 PLW-06002: Unreachable code
plw*.sql
Copyright 2000-2007 Steven Feuerstein - Page 35
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 36
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_totalsISBEGIN$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 37
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:
This will be most beneficial when we can use it in multiple versions of Oracle!
$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
Copyright 2000-2007 Steven Feuerstein - Page 38
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
ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 3;
11g_inline.sql
Copyright 2000-2007 Steven Feuerstein - Page 39
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
11g_inline.sql
Copyright 2000-2007 Steven Feuerstein - Page 40
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 41
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 42
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 43
What is a collection?
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.
abc def sf q rrr swq...1 2 3 4 22 23
Copyright 2000-2007 Steven Feuerstein - Page 44
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 45
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 46
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 47
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 48
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 49
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 50
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.sqlnocopy*.*
Copyright 2000-2007 Steven Feuerstein - Page 51
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 52
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 53
Function
PGA
Data Caching with PL/SQL Collections
First access
Subsequent accesses
PGAFunction
Database/ SGA
Not in cache;Request datafrom database
Pass Datato Cache
Application
Application Requests Data
Data retrieved from cache Data returned
to application
Application
Application Requests Data
Data returned to application
Data retrieved from cache
Database/ SGA
Data found incache. Databaseis not needed.
emplu.pkgemplu.tst
Copyright 2000-2007 Steven Feuerstein - Page 54
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 55
New indexing capabilities 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...)
Oracle9i Release 2
Copyright 2000-2007 Steven Feuerstein - Page 56
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 NUMBER INDEX BY employee.last_name%TYPE; TYPE array_t8 IS TABLE OF NUMBER INDEX BY types_pkg.subtype_t;
Oracle9i Release 2
Copyright 2000-2007 Steven Feuerstein - Page 57
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*.sqlassoc_array_perf.tst
int_to_string_indexing.sql
Copyright 2000-2007 Steven Feuerstein - Page 58
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; END mark_as_used;END string_tracker;
string_tracker1.*
Copyright 2000-2007 Steven Feuerstein - Page 59
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.
Demonstration package:assoc_array5.sql
Comparison of performance of different approaches:
vocab*.*
Generate a caching package:genaa.sqlgenaa.tst
Copyright 2000-2007 Steven Feuerstein - Page 60
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.
Oracle9i
Copyright 2000-2007 Steven Feuerstein - Page 61
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 62
The String Tracker package (V2)
CREATE OR REPLACE PACKAGE BODY string_trackerIS 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 63
Other multi-level collection examples
Multi-level collections with intermediate records and objects.
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. Four-level nested collection used to track
arguments for a program unit.– Automatically analyze ambiguous overloading.
multdim*.*
ambig_overloading.sqlOTN: OverloadCheck
multilevel_collections.sql
Copyright 2000-2007 Steven Feuerstein - Page 64
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 65
Nested Tables unveil their 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.
Oracle10g
Copyright 2000-2007 Steven Feuerstein - Page 66
Check for equality and inequality
Just use the basic operators….and NULLs have the usual disruptive impact.
Oracle10g
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.sql10g_compare2.sql
10g_compare_old.sql
Copyright 2000-2007 Steven Feuerstein - Page 67
UNION, INTERSECT, MINUS
Straightforward, with the MULTISET keyword.
Oracle10g
BEGIN our_favorites := my_favorites MULTISET UNION dad_favorites; our_favorites := my_favorites MULTISET UNION DISTINCT dad_favorites; our_favorites := my_favorites MULTISET INTERSECT dad_favorites; our_favorites := dad_favorites MULTISET EXCEPT my_favorites;END;
10g_setops.sql10g_string_nt.sql10g_favorites.sql
10g*union*.sql
SQL: UNION
SQL: UNION ALL
SQL: INTERSECT
SQL: MINUS
Copyright 2000-2007 Steven Feuerstein - Page 68
Distinct sets of values
Use the SET operator to work with distinct values, and determine if you have a set of distinct values.
Oracle10g
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.sql10g_favorites.pkg
Copyright 2000-2007 Steven Feuerstein - Page 69
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 70
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 71
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 seminarwww.oracleplsqlprogramming.com/resources.html
Copyright 2000-2007 Steven Feuerstein - Page 72
>> 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 73
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
DECLARE 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;
plvtmr.*tmr.ot
this_user*.*
Copyright 2000-2007 Steven Feuerstein - Page 74
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 75
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 76
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.
11g
Copyright 2000-2007 Steven Feuerstein - Page 77
Function Result Cache Example
CREATE OR REPLACE PACKAGE emplu11gIS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE;END emplu11g;
CREATE OR REPLACE PACKAGE BODY emplu11gIS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE RELIES_ON (employees) IS onerow_rec employees%ROWTYPE; BEGIN SELECT * INTO onerow_rec FROM employees WHERE employee_id = employee_id_in;
RETURN onerow_rec; END onerow;END emplu11g;
11g_emplu*.*
The specification must indicate you are using a result cache.
The body specifies the "relines on" dependencies, if any.
Copyright 2000-2007 Steven Feuerstein - Page 78
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
11g
Copyright 2000-2007 Steven Feuerstein - Page 79
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 80
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);
UPDATE employee SET salary = rec.salary WHERE employee_id = rec.employee_id; END LOOP;END upd_for_dept;
Row by row processing: simple and elegant but inefficient
Copyright 2000-2007 Steven Feuerstein - Page 81
Oracle server
PL/SQL Runtime Engine SQL Engine
PL/SQL blockProcedural statement executor
SQL statement executor
FOR rec IN emp_cur LOOP UPDATE employee SET salary = ... WHERE employee_id = rec.employee_id;END LOOP;
Performance penalty Performance penalty for many “context for many “context switches”switches”
Row by row processing of DML in PL/SQL
Copyright 2000-2007 Steven Feuerstein - Page 82
Bulk processing with FORALLOracle server
PL/SQL Runtime Engine SQL Engine
PL/SQL blockProcedural statement executor
SQL statement executor
FORALL indx IN list_of_emps.FIRST.. list_of_emps.LAST UPDATE employee SET salary = ... WHERE employee_id = list_of_emps(indx);
Fewer context switches,Fewer context switches,same SQL behaviorsame SQL behavior
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Copyright 2000-2007 Steven Feuerstein - Page 83
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 84
Use BULK COLLECT INTO for Queries
DECLARE TYPE employees_aat IS TABLE OF employees%ROWTYPE INDEX BY BINARY_INTEGER;
l_employees employees_aat;BEGIN SELECT * BULK COLLECT INTO l_employees FROM employees; FOR indx IN 1 .. l_employees.COUNT LOOP process_employee (l_employees(indx)); END LOOP;END;
bulkcoll.sqlbulktiming.sql
Declare a collection of
records to hold the queried data.
Use BULK COLLECT to
retrieve all rows.
Iterate through the 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 85
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; LOOP FETCH emps_in_dept_cur BULK COLLECT INTO emps LIMIT 1000;
EXIT WHEN emps.COUNT = 0;
process_emps (emps); END LOOP;END bulk_with_limit;
Use the LIMIT clause with the INTO to manage the amount of memory used with the BULK COLLECT operation.
Definitely the preferred approach in production applications with large or varying datasets.
bulklimit.sql
Copyright 2000-2007 Steven Feuerstein - Page 86
Use the FORALL Bulk Bind Statement Instead of executing repetitive, individual DML statements, you
can write your code like this:
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.
PROCEDURE upd_for_dept (...) ISBEGIN 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;
bulktiming.sqlbulk_rowcount.sql
bulkexc.sql
Copyright 2000-2007 Steven Feuerstein - Page 87
Better Exception Handlingfor 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 SAVE EXCEPTIONS INSERT INTO book values (books_in(indx));EXCEPTION WHEN BULK_ERRORS THEN FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT LOOP log_error (SQL%BULK_EXCEPTIONS(indx)); END LOOP;END;
Allows processing of all rows, even after an error
occurs.
New cursor attribute, a pseudo-
collection
bulkexc.sql
Oracle9i
Copyright 2000-2007 Steven Feuerstein - Page 88
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
in behavior. dbms_errlog.*save_exc_vc_dbms_errlog.sql
cfl_to_bulk7.sql
Copyright 2000-2007 Steven Feuerstein - Page 89
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 90
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 91
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
11g
ALTER SESSION SET PLSQL_CODE_TYPE = 'NATIVE';
Copyright 2000-2007 Steven Feuerstein - Page 92
>> 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 93
The Compound Trigger
– Rather than manage multiple triggers on the same table, you can join all trigger operations into a single compound trigger.
– Avoidance of mutating trigger errors is now much simpler and more straightforward.
11g
CREATE TRIGGER full_mfe_excuse_transactionBEFORE UPDATE ON mfe_customersCOMPOUND TRIGGER ... declare variables here ...
BEFORE STATEMENT IS BEGIN ... END BEFORE STATEMENT;
BEFORE ROW IS BEGIN ... END BEFORE ROW;
AFTER ROW IS BEGIN ... END AFTER ROW;
AFTER STATEMENT IS BEGIN ... END AFTER STATEMENT;END full_mfe_excuse_transaction;
mutating.sql11g_compound_mutating.sql
Copyright 2000-2007 Steven Feuerstein - Page 94
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_adjustBEGIN ...END;
11g
multiple_triggers.sqltrigger_conflict.sql
Copyright 2000-2007 Steven Feuerstein - Page 95
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 ROWBEGIN :NEW.employee_id := my_seq.NEXTVAL;END;/
11g
11g_native_sequence.sql
Copyright 2000-2007 Steven Feuerstein - Page 96
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;
11g
Copyright 2000-2007 Steven Feuerstein - Page 97
Reference fields of records in FORALL
Prior to 11g, you could not reference a field of a record in FORALL.
SQL> DECLARE 2 TYPE two_columns_rt IS RECORD ( 3 employee_id employees.employee_id%TYPE 4 , salary employees.salary%TYPE 5 ); 6 TYPE id_name_list_tt IS TABLE OF two_columns_rt INDEX BY PLS_INTEGER; 7 l_list id_name_list_tt; 8 BEGIN 9 SELECT employee_id, salary 10 BULK COLLECT INTO l_list FROM employees; 11 12 FOR idx IN 1 .. l_list.COUNT LOOP 13 l_list (idx).salary := apply_cola (l_list (idx).salary); 14 END LOOP; 15 16 FORALL idx IN 1 .. l_list.COUNT 17 UPDATE employees SET salary = l_list (idx).salary 18 WHERE employee_id = l_list (idx).employee_id; 19 END; 20 / UPDATE employees SET salary = l_list (idx).salary *ERROR at line 17:PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND
Instead, you have to break out the
data into separate collections.
Very irritating.
Oracle11g now lets you do this.
BUT...It is not
documented!
11g_field_of_record.sql
11g
Copyright 2000-2007 Steven Feuerstein - Page 98
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.
11g
Copyright 2000-2007 Steven Feuerstein - Page 99
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 100
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 101
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 102
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 103
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 ' || l_type || ' ' || l_name
'BEGIN ' || l_proc_name || ' (' || l_parameters || '); END;'
Copyright 2000-2007 Steven Feuerstein - Page 104
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 105
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 106
NDS or DBMS_SQL: Which should you use?
Reasons to go with NDS:– Ease of use– Works with all SQL
datatypes (including user-defined object and collection types)
– Fetch into records and collections of records
– Usually faster runtime performance
Why You'd Use DBMS_SQL:– Method 4 Dynamic SQL– DESCRIBE columns of
cursor– SQL statements larger than
32K– Better reuse of parsed SQL
statements -- persistent cursor handles!
Bottom line: NDS should be your first choice.
tabcount.sftabcount81.sf
Copyright 2000-2007 Steven Feuerstein - Page 107
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 108
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_USERIS CURSOR type_cur IS SELECT object_type FROM USER_OBJECTS WHERE object_name LIKE UPPER (nm); type_rec type_cur%ROWTYPE;BEGIN OPEN type_cur; FETCH type_cur INTO type_rec; IF type_cur%FOUND THEN EXECUTE IMMEDIATE 'DROP ' || type_rec.object_type || ' ' || nm; END IF;END;
dropwhatever.spcreind81.sphealth$.pkgsettrig.sp
Copyright 2000-2007 Steven Feuerstein - Page 109
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;
dml_str VARCHAR2 (200); loc_array loc_array_type;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;
Different table for
each location
Copyright 2000-2007 Steven Feuerstein - Page 110
Method 3: COUNT(*) For Any Table
Here's a handy and simple utility based on NDS:
IF tabCount ('citizens', 'insured = ''NO''') > 40,000,000THEN DBMS_OUTPUT.PUT_LINE ( 'Not the best health care system in the world....');END IF;
tabcount81.sftabcount.sf
CREATE OR REPLACE FUNCTION tabCount ( tab IN VARCHAR2, whr IN VARCHAR2 := NULL, sch IN VARCHAR2 := NULL) RETURN INTEGERIS retval INTEGER;BEGIN EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || NVL (sch, USER) || '.' || tab || ' WHERE ' || NVL (whr, '1=1') INTO retval; RETURN retval;END;
Specify schema, table and WHERE clause...
Copyright 2000-2007 Steven Feuerstein - Page 111
Quiz!
PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN IF line_in = 1 THEN process_line1; END IF; IF line_in = 2 THEN process_line2; END IF; ... IF line_in = 22045 THEN process_line22045; END IF;END;
What's wrong with this code?
How would you fix it?
Copyright 2000-2007 Steven Feuerstein - Page 112
From 22,000 lines of code to 1!
Identify the pattern and resolve it either with reusable modules or dynamic abstractions.
PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN IF line_in = 1 THEN process_line1; END IF; IF line_in = 2 THEN process_line2; END IF; ... IF line_in = 22045 THEN process_line22045; END IF;END;
PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN EXECUTE IMMEDIATE 'BEGIN process_line'|| line_in ||'; END;';END;
dynplsql.txt
Copyright 2000-2007 Steven Feuerstein - Page 113
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.spdynplsql_nolocal.sql
Copyright 2000-2007 Steven Feuerstein - Page 114
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.pkgdyncalc.pkg
str2list.*filepath*.pkg
Copyright 2000-2007 Steven Feuerstein - Page 115
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.sqlsql_guard.*
Copyright 2000-2007 Steven Feuerstein - Page 116
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 117
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.spintab9i.tst
Copyright 2000-2007 Steven Feuerstein - Page 118
Pseudo-code flow for DBMS_SQL implementation
BEGIN FOR each-column-in-table LOOP add-column-to-select-list; END LOOP;
DBMS_SQL.PARSE (cur, select_string, DBMS_SQL.NATIVE);
FOR each-column-in-table LOOP DBMS_SQL.DEFINE_COLUMN (cur, nth_col, datatype); END LOOP;
fdbk := DBMS_SQL.EXECUTE (cur); LOOP fetch-a-row; FOR each-column-in-table LOOP DBMS_SQL.COLUMN_VALUE (cur, nth_col, val); END LOOP; END LOOP;END;
Build the SELECT list
Define each column
Extract each value
Parse the variable SQL
Execute the query
Lots of code, but relatively straightforwardintab.sp
Copyright 2000-2007 Steven Feuerstein - Page 119
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 120
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 121
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 , l_lines , l_lines.FIRST , l_lines.LAST , TRUE , DBMS_SQL.native ); DBMS_SQL.close_cursor (l_cur); exec_ddl_from_file.sql
You can specify a subset of lines in
the array.
Copyright 2000-2007 Steven Feuerstein - Page 122
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.pkgdesccols.tst
Copyright 2000-2007 Steven Feuerstein - Page 123
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.
11g
Copyright 2000-2007 Steven Feuerstein - Page 124
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;
l_key_data key_data_cur_t;BEGIN DBMS_SQL.parse (c => cur_num , language_flag => DBMS_SQL.native , STATEMENT => l_query ); DBMS_SQL.bind_variable (cur_num, ':d', department_id); DBMS_SQL.bind_variable (cur_num, ':s', salary); dummy := DBMS_SQL.EXECUTE (cur_num);
l_curvar := DBMS_SQL.to_refcursor (cur_num);
FETCH l_curvar BULK COLLECT INTO l_key_data;
CLOSE l_curvar;END;
Start with DBMS_SQL,
leveraging the flexibility of its low-level API.
Use cursor variables to
easily transfer data to the collection
11g
Copyright 2000-2007 Steven Feuerstein - Page 125
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 126
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 127
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_ntIS retval names_nt := names_nt ();BEGIN retval.EXTEND (count_in);
FOR indx IN 1 .. count_in LOOP retval (indx) := base_name_in || ' ' || indx; END LOOP;
RETURN retval;END lotsa_names; tabfunc_scalar.sql
SELECT column_value FROM TABLE ( lotsa_names ('Steven' , 100)) names;
COLUMN_VALUE ------------Steven 1 ... Steven 100
Copyright 2000-2007 Steven Feuerstein - Page 128
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), pricedate DATE, pricetype VARCHAR2(1), price NUMBER)/
tabfunc_streaming.sql
Copyright 2000-2007 Steven Feuerstein - Page 129
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_pkgIS 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;/
tabfunc_streaming.sql
Copyright 2000-2007 Steven Feuerstein - Page 130
Use pipelined functions to enhance performance.
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.
CREATE FUNCTION StockPivot (p refcur_pkg.refcur_t) RETURN TickerTypeSet PIPELINED
Copyright 2000-2007 Steven Feuerstein - Page 131
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 132
Piping rows out from a pipelined function
CREATE FUNCTION stockpivot (p refcur_pkg.refcur_t) RETURN tickertypeset PIPELINED IS out_rec tickertype := tickertype (NULL, NULL, NULL); in_rec p%ROWTYPE;BEGIN LOOP FETCH p INTO in_rec; EXIT WHEN p%NOTFOUND; out_rec.ticker := in_rec.ticker; out_rec.pricetype := 'O'; out_rec.price := in_rec.openprice;
PIPE ROW (out_rec); END LOOP; CLOSE p;
RETURN;END;
tabfunc_setup.sqltabfunc_pipelined.sql
Add PIPELINED keyword to header
Pipe a row of data back to calling block
or query
RETURN...nothing at all!
Copyright 2000-2007 Steven Feuerstein - Page 133
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 134
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 135
>> Object-Oriented PL/SQL
Object Types and Object-Oriented Development
in PL/SQL
Copyright 2000-2007 Steven Feuerstein - Page 136
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 137
A Very Simple Object Type Example
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.
CREATE TYPE food_t AS OBJECT ( name VARCHAR2(100), food_group VARCHAR2 (100), grown_in VARCHAR2 (100));
Attributes
Copyright 2000-2007 Steven Feuerstein - Page 138
Working with Simple Objects
DECLARE my_favorite_vegetable food_t := food_t ('Brussel Sprouts',
'VEGETABLE', 'Farm,Greenhouse,Backyard');BEGIN DBMS_OUTPUT.put_line ( my_favorite_vegetable.name);
my_favorite_vegetable.food_group := 'SATISFACTION';
IF INSTR ( my_favorite_vegetable.grown_in, 'yard') > 0 THEN order_seeds (my_favorite_vegetable); END IF;END;
Create a new object with a constructor.
Read an attribute value
Modify an attribute value
Pass an object as a parameter
objtype.sql
Copyright 2000-2007 Steven Feuerstein - Page 139
"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 140
An Object Type with Methods
The timer object calculates elapsed time.
It consists of six attributes and all three types of methods.
CREATE TYPE tmr_t AS OBJECT ( starttime INTEGER , endtime INTEGER , startcputime INTEGER , endcputime INTEGER , repetitions INTEGER , NAME VARCHAR2 (2000)
, MEMBER PROCEDURE go , MEMBER PROCEDURE STOP (text IN VARCHAR2) , MEMBER FUNCTION timing RETURN INTEGER , MEMBER FUNCTION cputiming RETURN INTEGER , MEMBER FUNCTION timing_desc RETURN VARCHAR2 , STATIC FUNCTION make (name_in IN VARCHAR2) , CONSTRUCTOR FUNCTION tmr_t ( SELF IN OUT tmr_t , NAME IN VARCHAR2 , repetitions IN INTEGER ) RETURN SELF AS RESULT);
Attributes
Methods
tmr.otthisuser.*
Copyright 2000-2007 Steven Feuerstein - Page 141
More on Constructor Functions
Always pass SELF as an IN OUT argument.– SELF is assumed for
member methods. Return SELF as
well. Populate SELF
inside the constructor.
CREATE TYPE tmr_t AS OBJECT (... CONSTRUCTOR FUNCTION tmr_t ( SELF IN OUT tmr_t,
name IN VARCHAR2, repetitions IN INTEGER ) RETURN SELF AS RESULT);
CREATE OR REPLACE TYPE BODY tmr_t AS CONSTRUCTOR FUNCTION tmr_t ( SELF IN OUT tmr_t,
name IN VARCHAR2, repetitions IN INTEGER ) RETURN SELF AS RESULT IS BEGIN SELf.repetitions := repetitions; SELF.name := name; RETURN; END;END;
Copyright 2000-2007 Steven Feuerstein - Page 142
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 143
Let's Build a Type Hierarchy
We have a three level hierarchy:– food is the root type.– desserts are a type of food– cakes are a type of dessert.
We will make cake the most specialized type of food allowed in the hierarchy.
food
dessert
cake
"root",supertypeof dessert
subtype offood,supertypeof cake
subtypeof dessert
food.ot
Copyright 2000-2007 Steven Feuerstein - Page 144
Creating a Simple Object Type Hierarchy
CREATE TYPE food_t AS OBJECT ( name VARCHAR2(100), food_group VARCHAR2 (100), grown_in VARCHAR2 (100)) NOT FINAL;
CREATE TYPE dessert_t UNDER food_t ( contains_chocolate CHAR(1), year_created NUMBER(4)) NOT FINAL;
CREATE TYPE cake_t UNDER dessert_t ( diameter NUMBER, inscription VARCHAR2(200));
NOT FINAL indicates that this type can be a supertype.
UNDER denotes that this type is a subtype.
An object instantiated from food_t has three attributes. A dessert object has five attributes. A cake has seven.
food.ot
Copyright 2000-2007 Steven Feuerstein - Page 145
Substitutability of Object Types
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.
"Any object of type cake is also a dessert, is also a food."
Copyright 2000-2007 Steven Feuerstein - Page 146
Populate an Object Table
Create a table of objects of type food (root type).
Populate it with objects at different levels in hierarchy.DECLARE my_favorite_vegetables food_t := 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 VALUES (cake_t ( 'Marzepan Delight', 'CARBOHYDRATE', 'bakery', 'N', 1634, 8, 'Happy Birthday!' ) );END;
CREATE TABLE sustenance OF food_t;
Substitution of subtypes
Use of constructor to initialize a variable
food.ot
Copyright 2000-2007 Steven Feuerstein - Page 147
Objects in a Collection
Create a table of objects of type food - root type.
DECLARE TYPE foodstuffs_nt IS TABLE OF food_t;
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' ) );BEGIN ...
Insert three different objects in the collection, each of a different type.
Declare a nested table
Copyright 2000-2007 Steven Feuerstein - Page 148
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 149
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 SET dessert = TREAT (dessert AS cake_t);
treat.sql
Copyright 2000-2007 Steven Feuerstein - Page 150
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;
CREATE TABLE meal ( served_on DATE, appetizer food_t, main_course food_t, dessert dessert_t ) COLUMN appetizer NOT SUBSTITUTABLE AT ALL LEVELS;
At the table level
For a single column
Copyright 2000-2007 Steven Feuerstein - Page 151
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 152
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, main_course food_t, dessert dessert_t ) COLUMN appetizer NOT SUBSTITUTABLE AT ALL LEVELS,
COLUMN dessert IS OF (ONLY cake_t) ;
Unconstrainednon-substitutability
Constrain to a single subtype
notsubst.sqlisof.sql
You can only constrain to a single type, not a list of types.
Copyright 2000-2007 Steven Feuerstein - Page 153
Creating "Template" Types
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.
CREATE TYPE Address_t AS OBJECT ( street VARCHAR2(1000), city VARCHAR2(30), ... ) NOT INSTANTIABLE NOT FINAL;
food2.ot
Copyright 2000-2007 Steven Feuerstein - Page 154
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 155
Overriding to Provide Specificity for Subtypes
Different calculations for desserts and cakes.
CREATE OR REPLACE TYPE BODY cake_t IS OVERRIDING MEMBER FUNCTION price RETURN NUMBER IS BEGIN RETURN ( 5.00
+ 0.25 * (LENGTH (SELF.inscription)) + 0.50 * diameter); END;END;
Generic dessert prices are determined by chocolate
content and age. Cake prices are driven by inscription length
and size..
food2.ot
CREATE OR REPLACE TYPE BODY dessert_tIS OVERRIDING MEMBER FUNCTION price RETURN NUMBER IS mult NUMBER := 1; BEGIN IF SELF.contains_chocolate = 'Y' THEN mult := 2; END IF; IF SELF.year_created < 1900 THEN mult := mult + 0.5; END IF; RETURN (10.00 * mult ); END;END;
Copyright 2000-2007 Steven Feuerstein - Page 156
Disallowing Overrides
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.
CREATE TYPE Person_t AS OBJECT ( ssn NUMBER, name VARCHAR2(30), address VARCHAR2(100), FINAL MEMBER PROCEDURE showInfo) NOT FINAL;
Copyright 2000-2007 Steven Feuerstein - Page 157
Requiring Overrides to Methods
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.
CREATE TYPE food_t AS OBJECT ( name VARCHAR2(100), food_group VARCHAR2 (100), grown_in VARCHAR2 (100), NOT INSTANTIABLE MEMBER FUNCTION price RETURN NUMBER ) NOT FINAL NOT INSTANTIABLE;
If any member is NOT INSTANTIABLE, then the
entire type must be declared the same way.
food2.ot
Copyright 2000-2007 Steven Feuerstein - Page 158
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
11g_gen_invoc.sql
Copyright 2000-2007 Steven Feuerstein - Page 159
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 160
Exploring Dynamic Polymorphism
CREATE TYPE food_t AS OBJECT ( ...attributes... MEMBER FUNCTION price RETURN NUMBER ) NOT FINAL;
CREATE TYPE dessert_t UNDER food_t ( ...attributes... OVERRIDING MEMBER FUNCTION price RETURN NUMBER ) NOT FINAL) ; CREATE TYPE cake_t UNDER dessert_t ( ...attributes... -- No price method of its own. );
The food and dessert types each have a price method, but cake does not.
It simply inherits the dessert method.
Copyright 2000-2007 Steven Feuerstein - Page 161
A Visual Representation
The root price function is over-ridden in the dessert subtype.
The cake subtype now simply inherits its price calculation from its dessert supertype.
food
dessert
cake
Price
Price
the "original"
An override
Inheritedcalculation
Copyright 2000-2007 Steven Feuerstein - Page 162
Dynamically Choosing the Right Method
DECLARE TYPE foodstuffs_nt IS TABLE OF food_t;
fridge foodstuffs_nt := foodstuffs_nt ( food_t ('Eggs benedict', ...), dessert_t ('Strawberries and cream', ...), cake_t ('Chocolate Supreme', ...));BEGIN FOR indx IN fridge.FIRST .. fridge.LAST LOOP DBMS_OUTPUT.put_line ( 'Price of ' || fridge (indx).NAME || ' = ' || fridge (indx).price); END LOOP;END;
A collection of foods is populated with three
different object types.
The price invocation is resolved at run-time, and
not necessarily as the food_t.price method.
food3.otfood4.otfood5.ot
dynpoly_overhead.tst
Copyright 2000-2007 Steven Feuerstein - Page 163
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 164
Object tables
You can create a table of objects, known as an object table.– Use special OF syntax to specify it.
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.
CREATE TABLE food_table OF food_t
SELECT VALUE (f).name FROM food_table
object_table.sql
Copyright 2000-2007 Steven Feuerstein - Page 165
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 166
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_tAS 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 167
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 168
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 169
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 170
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 + LENGTH (SELF.NAME)); END;END;
map_example.sql
Copyright 2000-2007 Steven Feuerstein - Page 171
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 172
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 173
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_insertINSTEAD OF INSERT ON images_vFOR EACH ROWBEGIN /* 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 174
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 175
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 176
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 177
>> 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 178
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 = /tmputl_file_dir = /accounts/newdev
Copyright 2000-2007 Steven Feuerstein - Page 179
Working with Database Directories
A Directory is a database "object", define with a CREATE statement.– You need CREATE ANY DIRECTORY privilege.
utlfile_92.sql
CREATE OR REPLACE DIRECTORY ERROR_LOG AS '/tmp/apps/log';
SELECT owner, directory_name, directory_path FROM ALL_DIRECTORIES;View those directories
to which you have access.
GRANT READ ON DIRECTORY error_log TO SCOTT;Grant READ or WRITE
privileges on a directory.
Copyright 2000-2007 Steven Feuerstein - Page 180
Opening a File
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...
DECLARE fid UTL_FILE.FILE_TYPE;BEGIN fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'W', max_linesize => 32767);END;
Copyright 2000-2007 Steven Feuerstein - Page 181
Reading from a File
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 status flag.
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;
exec_ddl_from_file.sqlgetnext.sp infile.sf
Copyright 2000-2007 Steven Feuerstein - Page 182
Writing to a File
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.
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;
create_file.spgenaa.sql
Copyright 2000-2007 Steven Feuerstein - Page 183
Closing a File
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.
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;
Copyright 2000-2007 Steven Feuerstein - Page 184
Copy a File
You can specify an OS directory or a database object of type DIRECTORY (as shown above)
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
Copyright 2000-2007 Steven Feuerstein - Page 185
Remove a File
If no error is raised, then you deleted successfully
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 removeEND;
fremove.sqlfileIO92.pkg
Copyright 2000-2007 Steven Feuerstein - Page 186
Rename/move a File
You specify target location and file nameDECLARE 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 renameEND;
frename.sqlfileIO92.pkg
Copyright 2000-2007 Steven Feuerstein - Page 187
Obtaining attributes of a file
CREATE OR REPLACE FUNCTION flength ( location_in IN VARCHAR2, file_in IN VARCHAR2) RETURN PLS_INTEGERIS TYPE fgetattr_t IS RECORD ( fexists BOOLEAN, file_length PLS_INTEGER, block_size PLS_INTEGER ); 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 ); RETURN fgetattr_rec.file_length;END flength;
How big is a file? What is its block size? Does the file exist?
All valuable questions. All answered with a call to
UTL_FILE.FGETATTR.
flength.sqlfileIO92.pkg
Copyright 2000-2007 Steven Feuerstein - Page 188
Encapsulate and Improve
The best way to take advantage of the new UTL_FILE features is to encapsulate or wrap them inside a layer of enhancing code.– Improve the error handling and reporting.– Hide the messy details of workarounds/patches.– Provide easy backups of files.
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!
fileIO92.pkgfileIO92.tst
JFile.javaxfile.pkg
Copyright 2000-2007 Steven Feuerstein - Page 189
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
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.
Long-standing challenge in PL/SQL:
How can I find the line number on which an error was raised in PL/SQL?
But in Oracle10g, we have "back trace"!
Copyright 2000-2007 Steven Feuerstein - Page 190
Letting the error go unhandled…
CREATE OR REPLACE PROCEDURE proc1 ISBEGIN 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 ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;END;/
ERROR at line 1:ORA-01403: no data foundORA-06512: at "SCOTT.PROC1", line 7ORA-06512: at "SCOTT.PROC2", line 8ORA-06512: at "SCOTT.PROC3", line 5ORA-06512: at line 3
Backtrace.sql
Copyright 2000-2007 Steven Feuerstein - Page 191
Displaying the “error stack” inside PL/SQL
CREATE OR REPLACE PROCEDURE proc3ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ( DBMS_UTILITY.FORMAT_ERROR_STACK);END;/
SQL> exec proc3calling proc2calling proc1running proc1ORA-01403: no data foundbacktrace.sql
Copyright 2000-2007 Steven Feuerstein - Page 192
Displaying the contents of BACKTRACE
CREATE OR REPLACE PROCEDURE proc3ISBEGIN 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 proc3calling proc2calling proc1running proc1Error stack at top level:ORA-06512: at "SCOTT.PROC1", line 5ORA-06512: at "SCOTT.PROC2", line 7ORA-06512: at "SCOTT.PROC3", line 5
backtrace.sqlbt.pkg
Copyright 2000-2007 Steven Feuerstein - Page 193
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 length.
– You can set the buffer size to UNLIMITED.
SET SERVEROUTPUT ON SIZE UNLIMITED
32K!
Copyright 2000-2007 Steven Feuerstein - Page 194
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 195
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.
Oracle10g
CALL utl_recomp.recomp_serial ('SCOTT'); CALL utl_recomp.recomp_parallel ('SCOTT', 4); recompile.sql
Copyright 2000-2007 Steven Feuerstein - Page 196
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.
Oracle10g
Copyright 2000-2007 Steven Feuerstein - Page 197
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 => '[email protected]' ,recipients => '[email protected], [email protected]' ,cc => '[email protected]' ,bcc => '[email protected]' ,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 198
Attachments and UTL_MAIL
You can attach RAW or VARCHAR2 content as an attachment (up to 32K).
BEGIN UTL_MAIL.send_attachment_raw ( sender => '[email protected]' ,recipients => '[email protected], [email protected]' ,cc => '[email protected]' ,bcc => '[email protected]' ,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 199
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 200
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 201
Some Free PL/SQL Resources
http://www.oracle.com/technology/tech/pl_sql/index.html
http://www.oracle.com/technology/pub/columns/plsql/index.html
http://tahiti.oracle.com/
http://www.ToadWorld.com/SF
http://quest-pipelines.com/
http://quest-pipelines.com/pipelines/dba/PLVision/plvision.htm
Oracle Technology Network PL/SQL page
OTN Best Practice PL/SQL
Oracle documentation – complete, online, searchable!
PL/SQL Obsession - my on-line portal for PL/SQL developers
Quest Pipelines
I Love PL/SQL and...help improve the PL/SQL language!
PL/Vision
http://ILovePLSQLAnd.net
Copyright 2000-2007 Steven Feuerstein - Page 202
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.