+ All Categories
Home > Documents > Oracle Advanced Techniques PL SQL

Oracle Advanced Techniques PL SQL

Date post: 04-Apr-2015
Category:
Upload: ankurgakhar112
View: 308 times
Download: 22 times
Share this document with a friend
269
03/12/22 Copyright 2001 Steven Feuerstein PL/SQL Advanced Techniques - page 1 Achieving PL/SQL Excellence Oracle PL/SQL Advanced Techniques Oracle7 thru Oracle8i Steven Feuerstein www.StevenFeuerstein.com www.Quest.com www.OReilly.com and contributions most excellent from Bill Pribyl and Dick Bolz
Transcript

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 1

Achieving PL/SQL Excellence

Oracle PL/SQLAdvanced Techniques

Oracle7 thru Oracle8i

Steven Feuersteinwww.StevenFeuerstein.com

www.Quest.comwww.OReilly.com

and contributions most excellent from Bill Pribyl and Dick Bolz

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 2

Objective & Outline Objective

– Expand your knowledge and awareness of important and new features of the PL/SQL language.

Outline– Building with Packages (Oracle7+)– PL/SQL Collections (Oracle7 and Oracle8+)– Cursor Variables (Oracle7 and Oracle8+)– Dynamic SQL: DBMS_SQL and Native Dynamic SQL (8i)– Calling Java from PL/SQL (Oracle8i) and C (Oracle8)– Oracle Advanced Queuing with DBMS_AQ (Oracle8)– Managing Large Objects with DBMS_LOB (Oracle8)– Other Oracle8i New Features

» Autonomous Transactions (Oracle8i)» Invoker Rights Model (Oracle8i)» Row Level Security: DBMS_RLS

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 3

Software Used in Training PL/Vision: a library of packages installed on top of PL/SQL.

– PL/Vision Lite - use it, copy, change it for free -- unless you build software to be sold commercially.

– Active PL/SQL Knowledge Base: contains PL/Vision Professional, the fully supported and enhanced version.

Demonstration scripts executed in the training can be found on the RevealNet PL/SQL Pipeline:– http://www.revealnet.com/Pipelines/PLSQL/index.htm– Archives surfboard, Miscellaneous, PL/SQL Seminar Files– See filedesc.doc for a listing of many of the files.

The PL/SQL IDE (Integrated Development Environment).– You no longer have to use SQL*Plus and a crude editor! Choose from

among the many listed in plsql_ides.txt.

plsql_ides.txt

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 4

Building with PL/SQL Packages

Achieving PL/SQL Excellence

Overview

Initialization section

Overloading

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 5

What is a Package? A collection of code elements, from procedures and

functions to TYPE, variable and cursor declarations.– Single-most important structure within PL/SQL, and almost

certainly one of the most under-utilized.– Conceptually very simple, it can take some time to fully grasp the

implications and potential of the package.

The method of choice by Oracle and other software developers for extending the PL/SQL language. – You will find packages in the database, in Oracle Developer/2000,

in Oracle Application Server.

Let’s review some of the benefits of packages.

tmr.pkgdbparm.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 6

When to Build a Package Join physically logically-related code.

– Can lead to performance improvements.– Puts more structure and organization in your body of code.

Improve transaction integrity by hiding data structures behind the package interface.– Instead of writing SQL directly in your programs, you call the

packaged procedures and functions instead.

Construct very flexible and usable utilities for developers.– There's a big difference between a bunch of separate programs and

a coherent, package-based "component".

watch.pkg

custrules.pkginsga.pkg

te_employee.pkste_employee.pkb

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 7

Package Initialization The initialization section is a block of code at the end of

the package body that is executed once per session, the first time any package element is referenced.– The PL/SQL runtime engine determines when and if this code

should be run.

Does the package have an

init section?

Program referencespackage elementthe first time in each session.

no

Run initializationcode.

yes

Complete requestfor packaged

element.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 8

Package Initialization Structure The initialization section:

– Is defined after and outside of any programs in the package.

– Is not required. In fact, most packages you build won't have one.

– Can have its own exception handling section.

Useful for:– Performing complex setting of default or

initial values.– Setting up package data which does not

change for the duration of a session.– Confirming that package is properly

instantiated.

PACKAGE BODY pkgIS PROCEDURE proc IS BEGIN END;

FUNCTION func RETURN BEGIN END;BEGIN END pkg;

BEGIN after/outside of any program

defined in the pkg.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 9

Configure Session with Init. SectionPACKAGE BODY sessinit IS /* No declared package elements at all! */BEGIN /* Get user preferences for this user. */ SELECT lov_flag, tb_flag, defprinter INTO show_lov, show_toolbar, printer FROM user_config WHERE user_id = USER;

EXCEPTION WHEN NO_DATA_FOUND THEN /* No record for this user. */ show_lov := 'Y'; show_toolbar := 'Y'; printer := 'lpt1';

WHEN OTHERS THEN RAISE_APPLICATION_ERROR (-20000, 'No profile for ' || USER);END sessinit;

PACKAGE sessinitIS show_lov CHAR(1); show_toolbar CHAR(1); printer VARCHAR2(60);END sessinit;

An unusual package!– Specification contains

only variables.

– Body contains only initialization section.

init.pkginit.tst

Also a package with many design flaws...

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 10

Populate Collections The PL/Vision Date package, PLVdate, employs several

PL/SQL tables to convert strings and to perform date arithmetic.– This increases the flexibility of the date conversion process.– The datemgr.pkg file demonstrates the basic technique (and the

reliance on an initialization section) used to achieve this flexibility.

BEGIN fmts(1) := 'DD-MON-RR'; fmts(2) := 'DD-MON-YYYY'; fmts(3) := 'DD-MON'; fmts(4) := 'MM/DD'; ... fmts(9) := 'MM/DD/YYYY'; fmts(10) := 'MMDDYYYY'; fmts(11) := 'YYYYMMDD'; fmts(12) := 'RRMMDD'; fmt_count := 12; END dt;

datemgr.pkgdates.sql

Initialization sectionpopulates a

PL/SQL table.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 11

Program Overloading When you overload programs, you give

two or more programs the same name.– You can overload modules in any declaration

section and in packages. myproc

myproc

myproc

Overloading is a critical feature when building comprehensive programmatic interfaces (APIs) or components using packages.– If you want others to use your code, you need

to make that code as smart and as easy to use as possible.

– Overloading transfers the "need to know" from the user to the overloaded program.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 12

Overloading in PL/SQL Built-ins PL/SQL uses overloading in many common functions.

– You just probably never gave it a thought, and took functions like TO_CHAR and TO_DATE totally for granted.

– But Oracle couldn't offer that level of convenience without overloading.

date_string := TO_CHAR (SYSDATE, 'MMDDYY');

number_string := TO_CHAR (10000);

Without overloading, you would have to deal with something like this:

date_string := TO_CHAR_FROM_DATE (SYSDATE, 'MMDDYY'); number_string := TO_CHAR_FROM_NUMBER (10000);

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 13

How Overloading Works For two or more modules to be overloaded, the compiler must be able

to distinguish between the two calls at compile-time. There are two different "compile times":– 1. When you compile the package or block containing the overloaded code.– 2. When you compile programs that use the overloaded code.

Distinguishing characteristics:– The formal parameters of overloaded modules must differ in number, order or

datatype family (CHAR vs. VARCHAR2 is not different enough).

– The programs are of different types: procedure and function.

Undistinguishing characteristics:– Functions differ only in their RETURN datatype.

– Arguments differ only in their mode (IN, OUT, IN OUT).

– Their formal parameters differ only in datatype and the datatypes are in the same family.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 14

Examples of Invalid OverloadingsPACKAGE too_similarIS PROCEDURE calc (reg_in IN CHAR); PROCEDURE calc (reg_in IN VARCHAR2); END too_many_cals;

PACKAGE only_returnsIS FUNCTION func1 (val IN VARCHAR2) RETURN DATE; FUNCTION func1 (val IN VARCHAR2) RETURN VARCHAR2; END only_returns;

PACKAGE param_modesIS PROCEDURE proc1 (val IN VARCHAR2); PROCEDURE proc1 (val IN OUT VARCHAR2); END param_modes;

Parameter data types

cause conflict.

Only differenceis function

RETURN type.

Only differenceis parameter

mode.

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

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

param_modes.proc1 (v_value);which one?

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 15

Overload Wherever Possible Supporting Many Data Combinations

– Apply the same action to different kinds or combinations of data. In this case, the overloading does not provide a single name for different activities, so much as providing different ways of requesting the same activity.

– The DBMS_OUTPUT.PUT_LINE procedure illustrates this technique -- and the PL/Vision p.l substitute does an even better job.

Fitting the Program to the User – To make your code as useful as possible, you may construct different

versions of the “same” program which correspond to different patterns of use.

Overloading by Type, not Value– A less common application of overloading. You use the type of data and not

its value to determine which of the overloaded programs should be executed.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 16

A "Classic" Overloaded Package

PACKAGE p IS PROCEDURE l (date_in IN DATE, mask_in IN VARCHAR2 := ‘Month DD, YYYY - HH:MI:SS PM'); PROCEDURE l (number_in IN NUMBER); PROCEDURE l (char_in IN VARCHAR2);

PROCEDURE l (char_in IN VARCHAR2, number_in IN NUMBER);

PROCEDURE l (char_in IN VARCHAR2, date_in IN DATE, mask_in IN VARCHAR2 := 'Month DD, YYYY - HH:MI:SS PM');

PROCEDURE l (boolean_in IN BOOLEAN);

PROCEDURE l (char_in IN VARCHAR2, boolean_in IN BOOLEAN);END p;

p.spsp.spb

Many different datatype combinations, allowing the user to pass data to the "display engine" without writing "pre-processing" code.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 17

Advantage of Extended Overloading

p.l ('So what is different?');

p.l (print_report_fl);

p.l (SQLERRM, SQLCODE);

p.l (SYSDATE);

•Extended overloadings are more likely to meet user needs.

DBMS_OUTPUT.PUT_LINE ('So what is different?');

DBMS_OUTPUT.PUT_LINE (TO_CHAR (SYSDATE, 'MM/DD/YY HH:MI:SS'));

DBMS_OUTPUT.PUT_LINE (SQLERRM || ': ' || TO_CHAR (SQLCODE));

IF print_report_flTHEN DBMS_OUTPUT.PUT_LINE ('TRUE');ELSE DBMS_OUTPUT.PUT_LINE ('FALSE'); END IF;

• Minimal overloading means lots of extra coding...

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 18

Fitting the Program to the User

A single piece of functionality, such as "display data" or "create a file", can be applied or needed under very different circumstances.

If you take these different circumstances into account when you design your package specification, the user of your package can benefit from writing less code.– Your code is a a more natural "fit" under a variety of

requirements.

In my experience, few developers are considerate enough of their users to try to anticipate their needs.– If you want to write software that is admired,

appreciated...and taken completely for granted, think about the way it will be used.

Writing "unnecessary" code? Time to overload!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 19

Creating a File for a Quick Touch Suppose a developer needs to create a file to be used as a "flag" in the operating system.

– She doesn't care what's in it. It just needs to be present.– Here is the code required by UTL_FILE:

DECLARE fid UTL_FILE.FILE_TYPE;BEGIN fid := UTL_FILE.FOPEN ('tmp/flags', 'exists.flg', 'W'); UTL_FILE.PUT_LINE (fid, 'blah'); UTL_FILE.FCLOSE (fid);END;

In other words, you have to declare the record to hold the file handle, even though you are simply going to close the file immediately after opening it.

Of course, sometimes you will want to create a file and then perform additional operations, so this is just the way it has to be, right? WRONG!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 20

Procedure and Function Overloaded Why not overload a "create file" program so that you can pick the one that most closely fits your situation?

Consider the PLVfile package of PL/Vision.

PACKAGE PLVfileIS /* Procedure */ PROCEDURE fcreate (file_in IN VARCHAR2, line_in IN VARCHAR2 := NULL);

/* Function */ FUNCTION fcreate (file_in IN VARCHAR2, line_in IN VARCHAR2 := NULL) RETURN UTL_FILE.FILE_TYPE;

END PLVfile;

Overloading of FCreate

DECLARE fid UTL_FILE.FILE_TYPE;BEGIN fid := PLVfile.fcreate ('temp.ini', v_user); PLVfile.put_line (fid, TO_CHAR (SYSDATE));

Use as Function

BEGIN PLVfile.fcreate ('exists.flg');END;

Use as Procedure

custrules.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 21

"By-Type" Overloading In some situations, the user does not need to pass data,

but the type of data. – For example, when you use DBMS_SQL to set up a dynamic

query, you must call the DEFINE_COLUMN procedure to define the datatype of the Nth column in the cursor.

There are three ways to accomplish this:– Don't overload. Define a different program name for each

datatype.– Pass a string “name” of the datatype. – Pass a piece of data of the right type.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 22

Options for Specifying Column Type Don't even bother overloading...

BEGIN DBMS_SQL.DEFINE_INTEGER_COLUMN (cur, 1); DBMS_SQL.DEFINE_VARCHAR2_COLUMN (cur, 2, 30);

BEGIN DBMS_SQL.DEFINE_COLUMN (cur, 1, 'NUMBER'); DBMS_SQL.DEFINE_COLUMN (cur, 2, 'STRING', 30);

BEGIN DBMS_SQL.DEFINE_COLUMN (cur, 1, DBMS_SQL.NUMBER_TYPE); DBMS_SQL.DEFINE_COLUMN (cur, 2, DBMS_SQL.VARCHAR2_TYPE, 30);

Pass a named constant...

Pass a literal value...

Now let's look at two examples of overloading by datatype...– DBMS_SQL.DEFINE_COLUMN– PLVgen.func

So many programnames to remember!

Nasty hard-coding...

Lotsa typing, lotsa names...

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 23

Defining Dynamic SQL Columns The DBMS_SQL.DEFINE_COLUMN procedure defines the datatype of a column.

– To make it easier to accomplish this task, you only need to pass a value -- any value -- of the correct type. – The three code blocks below are equivalent, from the perspective of DBMS_SQL.DEFINE_COLUMN.

BEGIN DBMS_SQL.DEFINE_COLUMN (cur, 1, 1); DBMS_SQL.DEFINE_COLUMN (cur, 2, 'a', 30);

BEGIN DBMS_SQL.DEFINE_COLUMN (cur, 1, DBMS_UTILITY.GET_TIME); DBMS_SQL.DEFINE_COLUMN (cur, 2, USER, 30);

BEGIN DBMS_SQL.DEFINE_COLUMN (cur, 1, v_empno); DBMS_SQL.DEFINE_COLUMN (cur, 2, v_ename, 30);

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 24

Generating Functions by Value In PLVgen, the user indicates the type of function to be generated by providing a value.

– The particular value itself is of no importance. Any number, any date, any string, any Boolean will do.

PACKAGE PLVgenIS PROCEDURE func (name_in IN VARCHAR2, type_in IN VARCHAR2);

PROCEDURE func (name_in IN VARCHAR2, type_in IN NUMBER);

PROCEDURE func (name_in IN VARCHAR2, type_in IN DATE);

SQL> exec plvgen.func ('total_salary', 1)

A number function, please!

SQL> exec plvgen.func ('last_date', SYSDATE)

A date function, please!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 25

The Frustrations of Overloading Watch out! An overloading can compile successfully, but

you might later found out that you cannot actually call any of the overloaded programs.

PACKAGE profitsIS PROCEDURE calc (comp_id_IN IN NUMBER);

PROCEDURE calc (comp_id_IN IN company.comp_id%TYPE);END;

In the above example, I rely on an anchored type (%TYPE) to establish the datatype of the second calc’s parameter.– When I compile profits, PL/SQL does not sense a conflict with

above overloading even though comp_id is a numeric column.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 26

Quiz! Nuances of Overloading

Can I overload two programs which have parameters that differ only by name, like calc_totals shown above?– If not, why not?– If so, how would you do it? (Don't peek at the next page!)

PACKAGE salesIS PROCEDURE calc_total (zone_in IN VARCHAR2);

PROCEDURE calc_total (reg_in IN VARCHAR2);

END sales;

BEGIN sales.calc_total ('NORTHWEST');

sales.calc_total ('ZONE2');END;

?sales.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 27

Using Named Notation

Explicit association between the formal parameter (the "name") with the actual parameter (the "value").

Advantages of named notation include:– Code is more "self-documenting". This is especially useful when working

with infrequently used built-in programs.– You can skip over (not specify values for) any IN parameters that have

default values. That way you don't have to know and pass default values.

<formal parameter name> => <expression>

DBMS_JOB.submit ( job => v_jobno, what => 'DBMS_DDL.ANALYZE_OBJECT ' || '(''TABLE'',''LOAD1'',''TENK''' || ',''ESTIMATE'',null,estimate_percent=>50);', next_date => TRUNC (SYSDATE + 1), interval => 'TRUNC(SYSDATE+1)');

namednot.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 28

PL/SQL Collections

Achieving PL/SQL Excellence

Collections are single-dimensioned lists of information.

Three types of collections:– Index-by tables (Oracle7 only, originally called PL/SQL tables)– Nested tables (Oracle8 and above)– Variable arrays (VARRAYs, Oracle8 and above)

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 29

When to Use Collections Maintain any kind of list of related information for use in

your programs.

Emulate bi-directional cursors, which are otherwise not supported in PL/SQL

Cache data in session-level memory for faster access.

Build hash tables (custom indexing structures).

Improve query performance by avoiding joins.

Avoid mutating table errors in database triggers.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 30

Index-By Tables

Characteristics of an index-by table:– Unbounded

» Practically speaking. Valid row numbers range: -2,147,483,647 to 2,147,483,647

» You will not actually create tables this large. Instead, this broad range allows you to employ the row number as an intelligent key.

– Sparse» Data does not have to be stored in consecutive rows of information.

– Homogeneous» Data in each row has the same structure.

– Available only in PL/SQL

TYPE <table_type> IS TABLE OF <datatype> INDEX BY BINARY_INTEGER;

DECLARE TYPE inmem_emp_t IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER; emp_copy inmem_emp_t;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 31

TYPEdeclaration

PACKAGE BODY family IS

TYPE child_list_type IS TABLE OF VARCHAR2 (30)INDEX BY BINARY_INTEGER;

children child_list_type;Variable

declaration

Index_by Tables

children

6412

6904 ‘Lisa Marie’

‘Gary Richard’

‘Barbara Anne’6306

‘Lisa Nadezhka’6904

6810 ‘Adam Russell’6412 ‘Gary Richard’

‘Barbara Anne’6306

childrenchildren (6810) := ‘Adam Russell;children (6904) := 'Lisa Nadezhka';

ComponentSelection

kid := children (4);

Error: NO_DATA_FOUND

datemgr.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 32

Nested Tables

Nested table characteristics– Homogeneous

» Each row contains the same structure of data.

– Unbounded, but only with explicit EXTEND requests» Practically speaking. Valid row numbers range: 1 to 2,147,483,647

– Initially dense, but can become sparse if you DELETE inner rows– Available both in PL/SQL and SQL (as a column in a table)– The order of elements is not preserved in the database

[CREATE OR REPLACE] TYPE <table_type> IS TABLE OF <datatype> [NOT NULL];

DECLARE TYPE when_t IS TABLE OF DATE; birthdays when_t;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 33

Lisa MarieGary Richard

BOND

Nested TablesCREATE OR REPLACETYPE child_table_type IS TABLE OF VARCHAR2 (30);

CREATE TABLE db_family (surname VARCHAR2 (30), kids child_table_type) NESTED TABLE kids STORE AS kids_ntab;

BOLZ

Barbara Annedb_family

For ORACLE's use only

surname kids

Max RichardEric Thomas

ntdemo.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 34

Variable Arrays

Characteristics of variable arrays:– Homogeneous

» Each row contains the same structure of data.

– Bounded» Upper limit established when the TYPE is defined. Maximum value: 2,147,483,647

– Dense» Never any gaps between defined rows, can be EXTENDed or TRIMmed.

– Available both in PL/SQL and SQL (as a column in a table)» And the order of elements are preserved in the database.

– Variable arrays are actually stored in the DB table

[CREATE OR REPLACE] TYPE <table_type> IS VARRAY (N) OF <datatype> [NOT NULL];

DECLARE TYPE numbers_t IS VARRAY (10) OF NUMBER; salaries numbers_t;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 35

Variable Arrays

vademo.sql

CREATE OR REPLACETYPE child_va_type IS VARRAY (8) OF VARCHAR2 (30);

CREATE TABLE db_family (surname VARCHAR2 (30), kids child_va_type);

BOND1 Eric Thomas2 Max Richard

BOLZ1 Barbara Anne2 Gary Richard3 Lisa Marie

db_family

surname kids

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 36

Defining Collections First, you define the TYPE of the collection.

– For index-by tables, this can only occur in a PL/SQL declaration section. Best option: package specification.

– For nested tables and VARRAYs, you can define the TYPE in the database with a CREATE statement, or in a PL/SQL declaration section.

Then you declare an instance of that type, a collection, from the TYPE.– You can declare multiple collections from that TYPE.

CREATE OR REPLACE PACKAGE tabtypesIS TYPE integer_ibt IS TABLE OF INTEGER INDEX BY BINARY_INTEGER; TYPE integer_nt IS TABLE OF INTEGER; TYPE integer_vat IS VARRAY(10) OF INTEGER; ...END tabtypes;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 37

Obtaining Collection Information

ALL_COLL_TYPES– The types you have created (or have access to) in the database

ALL_TYPE_ATTRS– Attributes of the data type used in the TYPE definition.– The code used to define the collection TYPE

There is no information in the data dictionary available for index-by tables.

colldd.sql

SELECT A.attr_name || ' - ' || A.attr_type_name Attributes FROM all_coll_types T, all_type_attrs A WHERE T.owner = USER AND T.owner = A.owner AND T.type_name IN ('NAMES_VT', 'TMRS_VT') AND T.elem_type_name = A.type_name;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 38

Initializing Collections Before you can use a collection, it must be initialized.

– Index-by tables are initialized automatically, empty when declared.– Nested tables and VARRAYs are atomically null. You must initialize

them explicitly with a constructor.

DECLARE TYPE numbers_t IS VARRAY (10) OF NUMBER; salaries numbers_t := numbers_t (100, 200, 300);BEGIN

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

TYPE defined in the database

TYPE defined in PL/SQL

CREATE TABLE employee_denorm ( employee_id INTEGER, salary_history numbers_t);

Collection used in a table

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 39

Collections of Composites Starting with Oracle 7.3, the “homogeneous” contents of an index-by table's row can be a record .

– Can easily create an index-by table with the same structure as a database table by declaring a record with %ROWTYPE.

Starting with Oracle8, the datatype for any of the collection types can also be an object.– But you cannot have nested composite datatypes.

DECLARE TYPE comp_rectype IS RECORD (comp_id company.company_id%TYPE, total_rev NUMBER);

TYPE comp_tabtype IS TABLE OF comp_rectype INDEX BY BINARY_INTEGER;

comp_tab comp_tabtype;BEGIN comp_tab(1).comp_id := 1005;

Here we have a three step process. Again,

consider putting TYPEs in database or

packages.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 40

Sparse is Nice The sparse characteristic of index-by tables and nested

tables can be put to good use.– In an index-by table, a row exists in the table only when a value is

assigned to that row. In this way, it is very similar to a database table.

Rows do not have to be defined sequentially. – You should not fill sequentially, unless the order in which items are

selected is of importance.

– Instead, consider using the row value as "smart data" for your application (primary key, order by date, etc.).

Especially handy when caching data from relational tables in user memory.– In almost every case, your collections will contain a row's worth of

information.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 41

Transferring DB Table to Collection

CREATE OR REPLACE PACKAGE psempIS TYPE emp_tabtype IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;

emp_tab emp_tabtype;END;

This package moves the entire contents of the emp table into its corresponding collection.

Some questions:– Why would I put this

collection table in a package?– When is the collection loaded

with the data? – What rows in that collection

are utilized?

CREATE OR REPLACE PACKAGE BODY psempIS BEGIN FOR rec IN (SELECT * FROM emp) LOOP emp_tab (rec.empno) := rec; END LOOP;END;

Initialization section of package

psemp.pkgpsemp.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 42

Collection Gotchas

EXTEND before assigning a value to a row.– Not necessary for index-by tables, but you must do it for VARRAYs and nested tables.

For index-by tables, you must reference existing rows or a NO_DATA_FOUND exception is raised.– Use the EXISTS method to determine if a row existed.– For VARRAYs and nested tables, once extended, the row exists, even if you haven't assigned

a value explicitly.

CREATE TYPE names_t IS TABLE OF VARCHAR2(30);/DECLARE greedy_ceos names_t := names_t ();BEGIN greedy_ceos(1) := 'Hamilton, Jordan';END;/

Error -6533!

You've got to EXTEND first!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 43

Handling Collection Gotchas

You can EXTEND one or more rows. – Assign a default value with a second, optional argument.– Pre-extending a large number of rows in advance can

improve performance.

Include a handler for NO_DATA_FOUND or use the EXISTS method to avoid these exceptions.

BEGIN -- Extend by 10 since I know I will need that many. -- Set the value of each new row to the contents of the first row. salaries.EXTEND (10, salaries(salaries.FIRST));

BEGIN IF salaries.EXISTS (v_employee_id) THEN -- We are OK. ELSE DBMS_OUTPUT.PUT_LINE ('Data for employee not available.'); END IF;

preextend.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 44

Collection Methods Obtain information about the collection

– COUNT returns number of rows currently defined in the table.– 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.

The built-in package plitblm (PL/sql Index-TaBLe Methods) defines these methods.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 45

The DELETE Method You can delete one or more rows from a collection using

DELETE:

BEGIN -- Delete all rows myCollection.DELETE;

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

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

DELETE releases memory, but you may also want to callDBMS_SESSION.FREE_UNUSED_USER_MEMORY.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 46

Navigating Through Collections Use FIRST and NEXT to move from beginning to end.

Use LAST and PRIOR to move from end to beginning.

rowind PLS_INTEGER := birthdays.FIRST; -- birthdays.LASTBEGIN LOOP EXIT WHEN rowind IS NULL;

DBMS_OUTPUT.PUT_LINE (birthdays(rowind).best_present);

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 47

Using Collections Inside SQL Nested tables and VARRAYs can be defined as columns of a

table and referenced directly within SQL.

You can also apply SQL operations to the contents of nested tables and VARRAYs with these operators:– THE - Maps a single column value in a single row to a virtual

database table– CAST - Maps a collection of one type to a collection of another type– MULTISET - Maps a database table to a collection– TABLE - Maps a collection to a database table

Index-by tables are programmatic constructs only. – You cannot make a direct reference to an index-by table in SQL.– Instead, so do indirectly with a PL/SQL function.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 48

Using Collections inside SQL

Lisa MarieGary Richard

BOND

BOLZ

Barbara Annedb_family

surname children

Max RichardEric Thomas

Lisa MarieGary RichardBarbara Annecolumn_value

SELECT column_valueFROM TABLE (SELECT children FROM db_family WHERE surname = 'BOLZ');

UPDATE TABLE (SELECT children FROM db_family WHERE SURNAME = 'BOLZ)SET column_value = 'Lisa Nadezhka'WHERE column_value = 'Lisa Marie');

Lisa NadezhkaGary RichardBarbara Anne

. . .

BOLZ

db_family

surname children

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 49

Using the THE Operator Use THE to manipulate (retrieve, INSERT, UPDATE, DELETE) contents of a nested table in a

database table.– Can only use with nested tables, not VARRAYs or index-by tables.– Only accessible from within SQL statements in PL/SQL.

CREATE TYPE action_list_t IS TABLE OF VARCHAR2(100);/CREATE TABLE inflation_beater ( focus_area VARCHAR2(100), activities action_list_t) NESTED TABLE activities STORE AS activities_tab;

SELECT VALUE (act) FROM THE (SELECT activities FROM inflation_beater WHERE focus_area = 'FORTUNE 100') act;

UPDATE THE (SELECT activities FROM inflation_beater WHERE focus_area = 'FORTUNE 100') SET COLUMN_VALUE = 'DISBAND OSHA' WHERE COLUMN_VALUE = 'SIDESTEP OSHA';

the.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 50

Using the TABLE and CAST Operators Use CAST to convert a collection from one type to another, TABLE to convert a TYPE into a database table.

– Cannot use with index-by tables.– Useful when you would like to apply SQL operations against a PL/SQL collection (ie, one not stored in a database table).

DECLARE nyc_devolution cutbacks_for_taxcuts := cutbacks_for_taxcuts ('Stop rat extermination programs', 'Fire building inspectors', 'Close public hospitals');BEGIN DBMS_OUTPUT.PUT_LINE ( 'How to Make the NYC Rich Much, Much Richer:'); FOR rec IN (SELECT COLUMN_VALUE ohmy FROM TABLE (CAST (nyc_devolution AS cutbacks_for_taxcuts))) LOOP DBMS_OUTPUT.PUT_LINE (rec.ohmy); END LOOP;END;

cast.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 51

Using the MULTISET Operator MULTISET is the inverse of TABLE, converting a set of data (table, view, query) into a VARRAY or nested table.

– Cannot use with index-by tables.– You can use MULTISET to emulate or transform relational joins into collections, with potential client-server performance impact.

DECLARE CURSOR bird_curs IS SELECT b.genus, b.species, CAST(MULTISET(SELECT bh.country FROM bird_habitats bh WHERE bh.genus = b.genus AND bh.species = b.species) AS country_tab_t) FROM birds b; bird_row bird_curs%ROWTYPE;BEGIN OPEN bird_curs; FETCH bird_curs into bird_row;END;

multiset.sql

Retrieves all detail information for the master in one trip.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 52

CREATE OR REPLACE PACKAGE ibtab IS FUNCTION rowval (indx IN PLS_INTEGER) RETURN DATE; PRAGMA RESTRICT_REFERENCES (rowval, WNPS, WNDS);END;

CREATE OR REPLACE PACKAGE BODY ibtab IS TYPE date_tab IS TABLE OF DATE INDEX BY BINARY_INTEGER; hiredates date_tab; FUNCTION rowval (indx IN PLS_INTEGER) RETURN DATE IS BEGIN RETURN hiredates (indx); END;END;

Referencing IB Tables inside SQL You can't directly reference an index-by table's contents

inside SQL.

Instead, call functions that retrieve the table's data, but hide the index-by table structure.

ibtab_in_sql.sql

Make accessible in SQL for

Oracle8 and below.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 53

Examples of Collections in Action

Emulation of bi-directional cursor operations

Avoid mutating table problems in database triggers.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 54

CREATE OR REPLACE PACKAGE bidirIS /* Iterate through rows in the result set */ PROCEDURE setRow (nth IN PLS_INTEGER); FUNCTION getRow RETURN employee_plus%ROWTYPE; PROCEDURE nextRow; PROCEDURE prevRow;END;

Bi-Directional Cursor Emulation Oracle does not yet support the ability to move back and

forth (and at random) through a cursor's result set.– A talked-about feature for Oracle9i -- nope, didn't make it!

Instead, deposit your data in a collection and then provide programs to access that data in the necessary fashion.

This is particularly useful (read: efficient) when you need to perform multiple passes against the data.

bidir.pkgbidir.tst

Notice that the collection itself is

hidden.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 55

The Mutating Table Problem

Row level triggers cannot query from or change the contents of the table to which it is attached; it is "mutating".

So what are you supposed to do when a row-level operation needs to "touch" that table?

UPDATE row 1

UPDATE row N

UPDATE emp SET sal = 1000 Statement Level

Row Level

Database triggers can be attached to the SQL statement and/or the individual row operations on a table.

Note: in Oracle8i, you can use autonomous

transactions to relax restrictions

associated with queries.

mutating.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 56

A Solution Based on Index-by Tables

Since you cannot perform the processing desired in the row-level trigger, you need to defer the action until you get to the statement level.

If you are going to defer the work, you have to remember what you needed to do. – an index-by table is an ideal repository for this reminder list.

1st row trigger fires

Nth row trigger fires

Work List(PL/SQL Table)

Statement Trigger

Writes to list

Writes to list

Process datain the list.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 57

An Example: Ranking Salespeople A table holds the rankings based on the amount of annual

sales of salespeople within a department.– As the sales amount is updated in this table, the rankings must also

change to show the new standings for that department only.

Salesperson ID Sales Amount RankDepartment ID

1055 64333 74055.88 3

1055 65709 144533.91

1055 65706 109000.25

1047 70904 65011.25

1

2

6

"Deferred work" is not only necessary, but preferable.– By storing the salesperson’s department ids as they change, we

then know which departments to re-rank.– We might update more than one department within a statement so

we must be able to retain multiple department numbers.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 58

Trigger Logic Required

CREATE OR REPLACE TRIGGER Rank_Sales_Strg AFTER INSERT OR UPDATE OR DELETE ON rank_salesBEGIN rank.rank_depts;END;

Statement Level Trigger

All details of theranking are hidden

in the package body.

CREATE OR REPLACE TRIGGER Rank_Sales_Rtrg AFTER insert OR update OF sales_amt ON rank_sales FOR EACH ROW WHEN (OLD.sales_amt != NEW.sales_amt)BEGIN rank.add_dept (:new.dept_id);END;

Row Level TriggerDoesn't fire unless

the sales_amtis actually changed.

ranking.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 59

The Ranking PackagePACKAGE rankIS PROCEDURE add_dept (dept_id_in IN INTEGER);

PROCEDURE rank_depts;END rank;

PACKAGE BODY rankIS in_process BOOLEAN := FALSE;

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

PROCEDURE add_dept (dept_id_in IN INTEGER) IS BEGIN IF NOT in_process THEN dept_tab (dept_id_in) := TRUE; END IF; END add_dept

Table holds indicator that department

needs re-ranking.

Create row to indicate department to be ranked.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 60

The Ranking Package, Continued

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

IF NOT in_process THEN in_process := TRUE; LOOP EXIT WHEN v_deptid IS NULL; perform_ranking (v_deptid); v_deptid := dept_tab.NEXT (v_deptid); END LOOP; END IF;

in_process := FALSE; dept_tab.DELETE; END rank_dept;

END rank;

Avoid recursiveexecution of logic

Row number isdepartment number.

Clean up fornext time.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 61

Which Collection Type Should I Use?

Index-by tables– Need to use in both Oracle7 and Oracle8 applications– Want to take advantage of sparse nature for "intelligent keys".

Nested tables– You want to store large amounts of persistent data in a column.– You want to use inside SQL.

VARRAYs– You want to preserve the order in which elements are stored.– Set of data is relatively small (avoid row chaining).– You want to use inside SQL.– You don't want to have to worry about sparseness.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 62

Tips for Using Collections Wrap access to your collections.

– In many cases, you will want to avoid direct access to (assigning and retrieving) rows in your collections.

– This will give you the flexibility to change your implementation.– You can also hide complex rules for setting the row number.

Get creative!– Don't always fill and use the index-by table sequentially.– If you can somehow translate your application data to an integer, it

can be used as a row number, and therefore offers indexed access.– Julian date formats and DBMS_UTILITY.GET_HASH_VALUE offer

two different methods.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 63

Cursor Variables

Achieving PL/SQL Excellence

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 64

Result Set

Result Set

Result Set

Result Set

Architecture of Cursor Variables

A cursor variable points to an underlying cursor object in the database.– The cursor object in turns points to (and keeps its place in) a result set.

The cursor variable can be passed between programs (even between, say, a Java servlet and a PL/SQL stored procedure).– Static SQL only -- until Oracle8i.

Cursor Object

Cursor Object

Cursor Variable

Cursor Variable

Hard-CodedCursor

Hard-CodedCursor

Cursor Variable

Cursor VariablePGA

SharedGlobalArea

With Hard-Coded Cursors With Cursor Variables

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 65

Benefits of Cursor Variables

Share cursor management between programs, even across the client-server divide.– You don't have to pass the result sets of a cursor in order to allow

the client-side program to have direct access to the data in the result set.

– Oracle Developer 2.1 utilizes cursor variables when you choose to construct a "base table block" around stored procedures instead of a database table.

Share the same code across multiple, different queries. – Since the cursor name is no longer hard-coded, you can use a

single block of code (say, a reporting program) against different queries.

– We will try out this technique at the end of the section.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 66

Cursor Variable ExampleDECLARE TYPE company_curtype IS REF CURSOR RETURN company%ROWTYPE;

company_curvar company_curtype;

company_rec company_curvar%ROWTYPE;

BEGIN

OPEN company_curvar FOR SELECT * FROM company;

FETCH company_curvar INTO company_rec;

CLOSE company_curvar;END;

Declare a variable cursor TYPE.

Declare cursor variable based on that type.

Declare a record from cursor variable.

OPEN cursor variable, specifying the query.

FETCH from the cursor variable.

Close thecursor variable.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 67

Explicit Cursors and Cursor Variables Both hard-coded cursors and cursor variables work with

static SQL.– The SQL is fixed at compile-time. – The difference is that with cursor variables, you get to decide which

static query is opened.

Many cursor operations are the same:– Close a variable cursor with the same syntax as that for static

cursors.– Use cursor attributes (%ISOPEN, %FOUND, %NOTFOUND,

%ROWCOUNT) with cursor variables (as of Release 2.3).– Fetch data from the cursor result set through a cursor variable with

the same syntax as that of static cursors.

Let’s focus on the new and different capabilities of cursor variables.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 68

Declaring Cursor Types and Variables Cursors are declared in two steps, just like programmer-

defined records and PL/SQL tables.– 1. Define a cursor TYPE -- either "weak" or "strong".– 2. Define a cursor variable.

DECLARE

TYPE weak_curtype IS REF CURSOR;

TYPE comp_curtype IS REF CURSOR RETURN company%ROWTYPE;

curcmp_new comp_curtype;

cur_any weak_curtype;BEGIN

Declare a WEAK referenced cursor TYPE.

Declare a STRONG referenced cursor TYPE.

Declare cursor variablesfrom the TYPEs.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 69

Strong vs. Weak Cursor Types A strong (or constrained) cursor type has a defined

return data specification.– Can only reference cursor objects which return the same data

specification, which can be any single SQL datatype or any previously defined record structure.

– Datatype mismatches are identified at compile time.

TYPE cur_typ_name IS REF CURSOR [ RETURN return_type ];

TYPE cur_typ_name IS REF CURSOR RETURN emp%ROWTYPE; /* Strong */

TYPE cur_typ_name IS REF CURSOR; /* Weak */

The weak (or unconstrained) cursor type does not have a RETURN clause.– It can reference any cursor object, be opened FOR any query.– Datatype mismatches can only be identified at runtime.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 70

Opening with the Cursor Variable

When you open a cursor variable (whether of the weak or strong variety), you must provide the SQL query that identifies the result set.– If the variable has not yet been assigned to cursor object, the

OPEN FOR statement implicitly creates an object for the variable.

– If the variable is already pointing to a cursor object, the OPEN FOR reuses the existing object and attaches the new query to that cursor object.

Remember, the cursor object is nothing more than a memory location. – It is maintained independently of the query itself.

OPEN cursor_name FOR select_statement;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 71

Opening with Strong Cursor Types

STRONG cursor data specifications must match or be compatible with the structure of the SELECT statement.– You can establish the return type based on a database table, a

cursor or a programmer-defined record.

DECLARE

TYPE emp_curtype IS REF CURSOR RETURN emp%ROWTYPE;

emp_curvar emp_curtype;

BEGIN OPEN emp_curvar FOR SELECT * from emp; ...END;

Match Needed

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 72

Opening with Weak Cursor Types

A weak cursor TYPE doesn't define the RETURN structure; you can associate any SELECT statement with a weak cursor variable.

FUNCTION open_emp_or_dept (get_type_in IN VARCHAR2) RETURN pkg.cv_typeIS retval pkg.cv_type;BEGIN IF get_type_in = ‘EMP’ THEN OPEN retval FOR SELECT * FROM emp; ELSIF get_type_in = ‘DEPT’ THEN OPEN retval FOR SELECT * FROM dept; END IF; RETURN retval;END;

PACKAGE pkg IS TYPE cv_type IS REF CURSOR;END;

Either query will "do".Verification will takeplace at the FETCH.

REF TYPE placed inpackage so that it

is "globally" available.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 73

Fetching from the Cursor Variable

Fetching with cursor variables follows the same rules as those with static cursors.

The INTO structure must match in number and datatype to:– FOR STRONG cursor types, it match the cursor type data

specification.– FOR WEAK cursor types, it match the OPEN FOR statement

structure.

Compatibility checks are performed prior to fetching row.– The ROWTYPE_MISMATCH exception is raised on failure.– Fetching can continue with a different INTO clause.

FETCH cursor_var_name INTO record_name;FETCH cursor_var_name INTO var_name, var_name, ...;

mismatch.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 74

When to Use Cursor Variables Make it easier for calling programs (especially non-PL/SQL

programs) to manipulate result sets.– JDBC recognizes cursor variables.

Define a base table block in Forms Builder (formerly Oracle Forms) on stored procedures rather than a table directly.

Use a single block of code to manipulate multiple queries.– With explicit cursors, you have to repeat the code for each cursor,

since cursor names are "hard coded".– You could use dynamic SQL to achieve this effect, but static cursors

are more efficient than dynamic SQL and cursor variables are less complicated than DBMS_SQL.

hccursor.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 75

Consolidating Different Cursors The following package specification hides the SQL behind a single open function.

– It also creates the data structures you will need to call the function.

CREATE OR REPLACE PACKAGE allcursIS bydept CONSTANT INTEGER := 1; bysal CONSTANT INTEGER := 2;

TYPE int_rt IS RECORD (key INTEGER);

TYPE cv_t IS REF CURSOR RETURN int_rt;

FUNCTION open (type_in IN INTEGER) RETURN cv_t;END;/

allcurrs.pkgallcurs.tstexplcv.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 76

Consolidating Different Cursors The open function simply opens FOR a different SELECT based on the

criteria passed to it.

CREATE OR REPLACE PACKAGE BODY allcursIS FUNCTION open (type_in IN INTEGER) RETURN cv_t IS retval cv_t; BEGIN IF type_in = bydept THEN OPEN retval FOR SELECT empno FROM emp ORDER BY deptno; ELSIF type_in = bysal THEN OPEN retval FOR SELECT empno FROM emp ORDER BY SAL; END IF; RETURN retval; END;END;/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 77

Hiding SQL Variations in Resulting Code The following block demonstrates that you can alter which SQL statement to query -- in

this case, change the ORDER BY clause -- without having to change the code you write.

DECLARE cv allcurs.cv_t; v_empno emp.empno%TYPE;BEGIN cv := allcurs.open (&1);

LOOP FETCH cv INTO v_empno; EXIT WHEN cv%NOTFOUND; p.l (v_empno); END LOOP;

CLOSE cv;END;/

"Report processor"is independent of

the particular SELECT

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 78

Cursor Variables

Flexibility– Choose which static SQL statement is executed at run-time.

Strong and Weak Types– Create REF CURSORs for specific queries, or a more general,

unconstrained type.

Hide Variations in Underlying SQL– You no longer have to repeat the same code for different cursors.

Improve Client-Side Access to Data– At least in Oracle Developer 2.1, you can build screens that access data

using a cursor variable-based procedural API.

Let'ssummarize

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 79

Achieving PL/SQL Excellence

Dynamic SQL and dynamic PL/SQL:– The DBMS_SQL package– Native dynamic SQL in Oracle8i

Dynamic SQL

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 80

Dynamic SQL and PL/SQL Execution

"Dynamic SQL" mean that you construct the SQL statement or PL/SQL block at runtime and then execute it. – Available in PL/SQL since Release 2.1 and DBMS_SQL.– Also supported with "native dynamic SQL" in Oracle8i.

What can you do with Dynamic SQL?

Build ad-hoc query and update applications.– Very common requirement on the Web.

Execute DDL inside PL/SQL programs.– Construct powerful DBA utilities; you no longer have to write SQL

to generate SQL to get your job done.

Execute dynamically-constructed PL/SQL programs.– One example: implement indirect referencing in PL/SQL.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 81

Four Methods of Dynamic SQL Method 1: non-queries without host variables, executed a single time.

Method 2: non-queries with a fixed number of host variables, execute one or more times.

Method 3: queries with a fixed number of items in the SELECT list and a fixed number of host variables.

Method 4: queries with a variable number of items in the SELECT list and/or non-queries with a variable number of host variables.

These methods are in increasing order of complexity. If you can recognize the types, you can more quickly figure out how to code your solution. – Different methods require the use of different programs in DBMS_SQL. – NDS does not support method 4.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 82

Native Dynamic SQL Prior to Oracle8i, you would use the DBMS_SQL built-in

package to execute dynamic SQL. – But this package is very complex, difficult to use, and relatively slow

(performance did improve significantly as of Oracle8).

The new "native dynamic SQL" or NDS of Oracle8i offers two native statements in the PL/SQL language to implement most of your dynamic SQL requirements:– EXECUTE IMMEDIATE <sql string>, used for DDL, DML and single

row fetches.– OPEN FOR <sql string>, used for multi-row queries.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 83

EXECUTE IMMEDIATE

Use this statement to execute any dynamic SQL statement (including a PL/SQL block) except for multi-row queries.

The INTO clause allows you to pass values from the select list of a single row query into local variables, including objects, collections and records.

The USING clause allows you to specify bind arguments or variables to be passed into the SQL string before execution.

EXECUTE IMMEDIATE sql-string

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

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 84

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... and not much of a democracy either!');END IF;

tabcount81.sfcompare with:

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 85

Other Execute Immediate Examples Perform an update...CREATE OR REPLACE PROCEDURE updnumval ( tab_in IN VARCHAR2, col_in IN VARCHAR2, start_in IN DATE, end_in IN DATE, val_in IN NUMBER) ISBEGIN EXECUTE IMMEDIATE 'UPDATE ' || tab_in || ' SET ' || col_in || ' = :val WHERE hiredate BETWEEN :lodate AND :hidate' USING val_in, start_in, end_in;END;

Pass in bind variables with USING clause.

Execute a stored procedure...PROCEDURE runprog (pkg_in IN VARCHAR2, name_in IN VARCHAR2) IS v_str VARCHAR2 (100);BEGIN v_str := 'BEGIN ' || pkg_in || '.' || name_in || '; END;'; EXECUTE IMMEDIATE v_str;EXCEPTION WHEN OTHERS THEN pl ('Compile Error "' || SQLERRM || '" on: ' || v_str); END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 86

PROCEDURE add_profit_source ( hosp_name IN VARCHAR2, pers IN Person, cond IN preexisting_conditions)ISBEGIN EXECUTE IMMEDIATE 'INSERT INTO ' || tabname (hosp_name) || ' VALUES (:revenue_generator, :revenue_inhibitors)' USING pers, cond;END;

Using Objects and Collections in NDS One of the key advantages to NDS over DBMS_SQL is that it

works with Oracle8 datatypes, including objects and collections.– No special syntax needed...– In the following example, the USING clause allows me to pass an

object and nested table to an INSERT statement with a variable table name.

health$.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 87

Multiple Row Queries and NDS Oracle extends the cursor variable feature of Oracle7 to

support multi-row dynamic queries.– Here is a simple utility the displays the values of any date, number or

string column in any table.

CREATE OR REPLACE PROCEDURE showcol ( tab IN VARCHAR2, col IN VARCHAR2, whr IN VARCHAR2 := NULL)IS TYPE cv_type IS REF CURSOR; cv cv_type; val VARCHAR2(32767); BEGIN OPEN cv FOR 'SELECT ' || col || ' FROM ' || tab || ' WHERE ' || NVL (whr, '1 = 1'); LOOP FETCH cv INTO val; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE (val); END LOOP; CLOSE cv;END;

showcol.spndsutil.pkg

Familiar cursor variable syntax!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 88

Some Fine Print for NDS You cannot pass schema elements (table names, column

names, etc.) through the USING clause.

You cannot pass the NULL literal directly in the USING clause. Instead, pass a variable with a NULL value.

The USING clause for a query can only have IN bind arguments.

You can have duplicate placeholders (for bind arguments).– If dynamic SQL, then you provide a value for each placeholder (by

position).– If dynamic PL/SQL, provide a value for each distinct placeholder (by

name).

str2list.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 89

Dynamic SQL using DBMS_SQL Prior to Oracle8i, the only way to perform dynamic SQL was

with the DBMS_SQL package.

DBMS_SQL is a very large and complex package, with many rules to follow and lots of code to write.

Supports all four methods of dynamic SQL, in particular method 4.

The overhead for using DBMS_SQL has decreased significantly in Oracle8 and again in Oracle8i.

You should only use DBMS_SQL when you cannot use NDS.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 90

Learning Through Examples DDL

– Create an index from within PL/SQL

DML– Update rows in a table

DML with binding– Update rows using bind variables

Queries– Method 3 and a dynamic WHERE clause

PL/SQL Version of "SELECT *"– Example of Method 4

PL/SQL– Create a generic calculation program

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 91

DDL with Dynamic SQL

Creates an index on any column(s) in any table in your schema.– Open a cursor, which will be used to execute the DDL

statement.

– Construct the DDL statement as a string.

– Parse and execute that DDL statement.

PROCEDURE create_index (index_in IN VARCHAR2, tab_in IN VARCHAR2, col_in IN VARCHAR2) IS cur INTEGER := DBMS_SQL.OPEN_CURSOR; fdbk INTEGER; DDL_statement VARCHAR2(200) := 'CREATE INDEX ' || index_in || ' ON ' || tab_in || ' ( ' || col_in || ')';BEGIN DBMS_SQL.PARSE (cur, DDL_statement, DBMS_SQL.NATIVE); fdbk := DBMS_SQL.EXECUTE (cur); DBMS_SQL.CLOSE_CURSOR (cur);END;

creind.sp

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 92

Updates with Dynamic SQL Update numeric column for specified employees.

CREATE OR REPLACE PROCEDURE updnumval ( col_in IN VARCHAR2, ename_in IN emp.ename%TYPE, val_in IN NUMBER)IS cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR; fdbk PLS_INTEGER;BEGIN DBMS_SQL.PARSE (cur, 'UPDATE emp SET ' || col_in || ' = ' || val_in || ' WHERE ename LIKE UPPER (''' || ename_in || ''')', DBMS_SQL.NATIVE);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_OUTPUT.PUT_LINE ('Rows updated: ' || TO_CHAR (fdbk));

DBMS_SQL.CLOSE_CURSOR (cur);END;

updnval1.sp

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 93

Updates with Bind Variables Update salaries for date range using binding.

CREATE OR REPLACE PROCEDURE updnumval ( col_in IN VARCHAR2, start_in IN DATE, end_in IN DATE, val_in IN NUMBER)IS cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR; fdbk PLS_INTEGER;BEGIN DBMS_SQL.PARSE (cur, 'UPDATE emp SET ' || col_in || ' = ' || val_in || ' WHERE hiredate BETWEEN :lodate AND :hidate', DBMS_SQL.NATIVE);

DBMS_SQL.BIND_VARIABLE (cur, 'lodate', start_in); DBMS_SQL.BIND_VARIABLE (cur, 'hidate', end_in);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_OUTPUT.PUT_LINE ('Rows updated: ' || TO_CHAR (fdbk)); DBMS_SQL.CLOSE_CURSOR (cur);END;

updnval2.spupdnval3.sp

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 94

Processing Flow for a Dynamic Query

Make sure SELECT iswell formed(PARSE)

Make sure SELECT iswell formed(PARSE)

Bind any variables(BIND_VARIABLE)(BIND_ARRAY)

Bind any variables(BIND_VARIABLE)(BIND_ARRAY)

Give cursor structure (DEFINE_COLUMN)(DEFINE_ARRAY)

Give cursor structure (DEFINE_COLUMN)(DEFINE_ARRAY)

Fill buffer with data(FETCH_ROWS)(EXECUTE_AND_FETCH)

Fill buffer with data(FETCH_ROWS)(EXECUTE_AND_FETCH)

Retrieve the data (COLUMN_VALUE)Retrieve the data (COLUMN_VALUE)

Fill cursor with data(EXECUTE)Fill cursor with data(EXECUTE)

Release cursor memory (CLOSE_CURSOR)Release cursor memory (CLOSE_CURSOR)

Allocate cursor memory(OPEN_CURSOR)Allocate cursor memory(OPEN_CURSOR)

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 95

Queries with Dynamic SQL Show employees using a dynamic WHERE clause...

CREATE OR REPLACE PROCEDURE showemps (where_in IN VARCHAR2 := NULL) IS cur INTEGER := DBMS_SQL.OPEN_CURSOR; rec emp%ROWTYPE; fdbk INTEGER;BEGIN DBMS_SQL.PARSE (cur, 'SELECT empno, ename FROM emp ' || ' WHERE ' || NVL (where_in, '1=1'), DBMS_SQL.NATIVE);

DBMS_SQL.DEFINE_COLUMN (cur, 1, 1); DBMS_SQL.DEFINE_COLUMN (cur, 2, 'a', 60);

fdbk := DBMS_SQL.EXECUTE (cur); LOOP EXIT WHEN DBMS_SQL.FETCH_ROWS (cur) = 0; DBMS_SQL.COLUMN_VALUE (cur, 1, rec.empno); DBMS_SQL.COLUMN_VALUE (cur, 2, rec.ename); DBMS_OUTPUT.PUT_LINE (TO_CHAR (rec.empno) || '=' || rec.ename); END LOOP;

DBMS_SQL.CLOSE_CURSOR (cur);END;

showemps.spshowemp2.spshowemps.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 96

Dynamic SELECT * FROM Any Table Method 4 example: the number of columns queried changes with each table.

– The resulting code is much more complicated.

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; LOOP fetch-a-row; FOR each-column-in-table LOOP DBMS_SQL.COLUMN_VALUE (cur, nth_col, val); END LOOP; END LOOP;END;

intab.sp

Very simplified pseudo-code

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 97

Using EXECUTE_AND_FETCH

Makes it easy to execute and fetch a single row from a query. – Very similar to the implicit SELECT cursor in native PL/SQL, which returns a

single row, raises NO_DATA_FOUND or raises the TOO_MANY_ROWS exception.

– If exact_match is TRUE, then EXECUTE_AND_FETCH will raise the TOO_MANY_ROWS exception if more than one row is fetched by the SELECT.

– Even if the exception is raised, the first row will still be fetched and available.

FUNCTION execute_and_fetch (cursor_in IN INTEGER, exact_match IN BOOLEAN DEFAULT FALSE) RETURN INTEGER;

numrows := DBMS_SQL.EXECUTE_AND_FETCH (cur);

numrows := DBMS_SQL.EXECUTE_AND_FETCH (cur, TRUE);

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 98

Dynamic Formula Execution Suppose I am building a user interface that allows a user to

select a formula for execution, and enter the arguments.– Using static PL/SQL, I would have to modify my screen every time a

new formula was added. – With DBMS_SQL, a single function will do the trick.

FUNCTION dyncalc ( oper_in IN VARCHAR2, nargs_in IN INTEGER := 0, arg1_in IN VARCHAR2 := NULL, arg2_in IN VARCHAR2 := NULL, arg3_in IN VARCHAR2 := NULL, arg4_in IN VARCHAR2 := NULL, arg5_in IN VARCHAR2 := NULL, arg6_in IN VARCHAR2 := NULL, arg7_in IN VARCHAR2 := NULL, arg8_in IN VARCHAR2 := NULL, arg9_in IN VARCHAR2 := NULL, arg10_in IN VARCHAR2 := NULL ) RETURN VARCHAR2;

dyncalc.sfdyncalc.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 99

More on Dynamic PL/SQL

Use BIND_VARIABLE to bind any placeholders in the string -- even OUT arguments which are not being bound to any values.

Use VARIABLE_VALUE to extract a value from any variable you have bound.

You must have a BEGIN-END around the code.

Possibilities inherent in dynamic PL/SQL are mind-boggling!

BEGIN cur := open_and_parse ('BEGIN get_max_sal (:deptin, :salout); END;');

DBMS_SQL.BIND_VARIABLE (cur, ‘deptin’, v_deptin); DBMS_SQL.BIND_VARIABLE (cur, ‘salout’, my_salary);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_SQL.VARIABLE_VALUE (cur, ‘salout’, my_salary);END;

dynplsql.sqldynplsql.spdynplsql.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 100

Indirect Referencing with Dyn PL/SQL Oracle Forms offers support for indirect referencing with the

NAME_IN and COPY built-ins.

PL/SQL does not support indirect referencing, but you can accomplish much of the same thing with dynamic PL/SQL execution. Here is an example of a "PL/SQL NAME_IN":

FUNCTION valbyname (nm IN VARCHAR2) RETURN VARCHAR2 IS v_cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR; fdbk PLS_INTEGER; retval PLV.dbmaxvc2;BEGIN DBMS_SQL.PARSE (v_cur, 'BEGIN :val := ' || nm || '; END;', DBMS_SQL.NATIVE); DBMS_SQL.BIND_VARIABLE (v_cur, 'val', 'a', 2000); fdbk := DBMS_SQL.EXECUTE (v_cur); DBMS_SQL.VARIABLE_VALUE (v_cur, 'val', retval); DBMS_SQL.CLOSE_CURSOR (v_cur); RETURN retval;END;

dynvar.pkgdynvar.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 101

DBMS_SQL Status Built-ins The package offers a set of modules to return information about the last-

operated cursor in your session.– Call these immediately after your usage to make sure they refer to your cursor.

IS_OPEN– Is the cursor already open?

LAST_ERROR_POSITION– Returns relative column position in cursor of text causing error condition.

LAST_ROW_COUNT– Returns the cumulative count of rows fetched from the cursor.

LAST_ROW_ID– Returns the ROWID of last row processed.

LAST_SQL_FUNCTION_CODE– Returns SQL function code of cursor.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 102

Working with LONG Values DBMS_SQL provides special procedures so that you can

extract values from a LONG column in a table.– DBMS_SQL.DEFINE_COLUMN_LONG– DBMS_SQL.COLUMN_VALUE_LONG

First, you define the column as a LONG,

Then you retrieve the column using the special COLUMN_VALUE_LONG variant.– Transfer the LONG contents into an index-by table so that you can

transfer a value of more than 32K bytes into your PL/SQL program.

dumplong.pkgdumplong.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 103

New DBMS_SQL Features

PL/SQL8Extensions toDBMS_SQL

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 104

New Features in DBMS_SQL

The functionality of DBMS_SQL has been extended in Oracle8 in several ways:– Parse very long SQL strings– Describe cursor columns– Use "array processing" to perform bulk updates,

inserts, deletes and fetches.– Support for RETURNING clause to avoid unnecessary

queries.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 105

Describing Cursor Columns

Before PL/SQL8, it was not possible to determine the datatypes of the columns defined in a cursor. – Now you can call the DBMS_SQL.DESCRIBE_COLUMNS.

Returns all of the column information in an index table of records.– The record TYPE is also defined in DBMS_SQL.

PROCEDURE DBMS_SQL.DESCRIBE_COLUMNS (c IN INTEGER, col_cnt OUT INTEGER, desc_t OUT DBMS_SQL.DESC_TAB);

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 106

Basic Steps to Describe Columns The following script shows the individual steps you will need to perform in

order to use this feature.

CREATE OR REPLACE PROCEDURE show_columnsIS cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR; cols DBMS_SQL.DESC_TAB; ncols PLS_INTEGER;BEGIN DBMS_SQL.PARSE (cur, 'SELECT hiredate, empno FROM emp', DBMS_SQL.NATIVE);

DBMS_SQL.DESCRIBE_COLUMNS (cur, ncols, cols);

FOR colind IN 1 .. ncols LOOP DBMS_OUTPUT.PUT_LINE (cols(colind).col_name); END LOOP; DBMS_SQL.CLOSE_CURSOR (cur);END;

desccols.pkgdesccols.tst showcols.sp

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 107

"Array Processing" in DBMS_SQL PL/SQL8 now allows you to specify the use of "arrays", i.e.,

index tables, when you perform updates, inserts, deletes and fetches.

Instead of providing a scalar value for an operation, you specify an index table. DBMS_SQL then repeats your action for every row in the table.

It really isn't "array processing". – In actuality, DBMS_SQL is executing the specified SQL statement N

times, where N is the number of rows in the table.

This technique still, however, can offer a significant performance boost over Oracle7 dynamic SQL.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 108

Recommendations for Dynamic SQL Bind (vs. concatenate) whenever possible.

– Increased chance of reusing parsed SQL, and easier code to write.– But remember: you cannot pass schema elements (table names,

column names, etc.) through the USING clause.

Encapsulate statements to improve error handling.– With NDS, only possible for statements that do not need USING and

INTO clauses, though you could write variations for those as well. – Encapsulate DBMS_SQL.PARSE so that you include the trace in that

program.

Use the Oracle8i invoker rights model whenever you want to share your dynamic SQL programs among multiple schemas.– Otherwise that SQL will be executed under the authority

of the owner of the code, not the invoker of the code. effdsql.sqlopenprse.pkgwhichsch.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 109

NDS or DBMS_SQL: Which is Best? Dynamic SQL and PL/SQL is very useful, but DBMS_SQL is hard to use. Both

implementations will still come in handy...– If, of course, you have upgraded to Oracle8i!

Major Advantages of NDS:– Ease of use– Performance– Works with all SQL

datatypes (including user-defined object and collection types)

– Fetch into records

When You'd Use DBMS_SQL:– Method 4 Dynamic SQL– DESCRIBE columns of cursor– SQL statements larger than 32K– RETURNING into an array– Reuse of parsed SQL statements– Bulk dynamic SQL– Available from client-side PL/SQL

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 110

Achieving PL/SQL Excellence

Oracle Advanced Queuing

The high performance, asynchronous, persistent messaging subsytem of Oracle

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 111

Who Needs Messaging? Everyone! In the distributed world of the Internet, systems are now

heavily reliant on the ability of components to communicate with each other in a dependable, consistent manner.

Distribution Center

Coffee BeansProducer Retailer

Consumer

Shipping Service

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 112

Applications Relying on Messaging

Stock trading system

Airline reservation system

Auction portals

Any e-commerce application

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 113

Key Features of Oracle AQ Leverage full power of SQL

– Messages are stored in database tables

Database high availability, scalability and reliability all carry over to queues– Strong history and retention– Backup and recovery– Comprehensive journaliing

Rich message content increases usefulness of queueing– Use object types to define highly structured payloads

New to Oracle8i, AQ now offers a publish/subscribe style of messaging between applications. – Rule-based subscribers, message propagation, the listen feature and

notification capabilities.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 114

Queue Monitor process

AQ architectural overview

Queue

“Producers”

Enqueued messages

“Consumers”

Message 4

Message 3

Message 2

Message1

Queue table

Dequeued messages

Messages include both control information and “payload” (content)

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 115

Oracle AQ Highlights In 8.0, AQ supports:

– Multiple queues– Resetting order and priority of queued items– Queue management using only SQL & PL/SQL– Multiple message recipients– Propagation of queue to remote servers

Oracle8i adds:– Rules-based publish & subscribe– Listening on multiple queues– Easier monitoring– Native Java interface

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 116

AQ Components The DBMS_AQ package offers enqueue and

dequeue capabilities

The DBMS_AQADM package provides administrative functionality to manage queues and queue tables.

Underlying database tables and views

The queue monitor (background process)– Set # of processes with the AQ_TM_PROCESSES

initialization parameter.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 117

DBMS_AQADM Highlights

CREATE_QUEUE_TABLE Assigns name, payload type, storage clause, sort column, whether multiple consumers

DROP_QUEUE_TABLE Drops table if all queues in the table have been stopped

CREATE_QUEUE Associates queue table with queue; assigns retry and retention properties to queue

DROP_QUEUE Drops a stopped queue

START_QUEUE Can also turn on/off enqueue and dequeue operations

STOP_QUEUE Stops queue, optionally waiting for outstanding transactions

ADD_SUBSCRIBER Adds an “agent” as a subscriber

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 118

BEGIN

DBMS_AQADM.CREATE_QUEUE_TABLE (queue_table => 'msg', queue_payload_type => 'message_type');

DBMS_AQADM.CREATE_QUEUE (queue_name => 'msgqueue', queue_table => 'msg');

DBMS_AQADM.START_QUEUE (queue_name => 'msgqueue');

END;

Creating Queue Tables and Queues

CREATE TYPE message_type AS OBJECT (title VARCHAR2(30), text VARCHAR2(2000));/

Define the "payload"

Create the queue table

Define a queue in the queue table

Start the queue

aq.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 119

The "operational package": DBMS_AQ DBMS_AQ is deceptively simple.

– Only two procedures, but lots of complexity buried inside the parameters of these procedures.

ENQUEUE puts a message into a specified queue, and returns a RAW message handle

DEQUEUE extracts a message from a specified queue

Parameters control message properties such as:– Visibility (ON_COMMIT or IMMEDIATE)– Priority– Delay– Expiration– Locking behavior

aq.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 120

Simple Enqueue Example

DECLARE queueopts DBMS_AQ.ENQUEUE_OPTIONS_T; msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; msgid aq.msgid_type; my_msg message_type;BEGIN my_msg := message_type ( 'First Enqueue', 'May there be many more...');

DBMS_AQ.ENQUEUE ( 'msgqueue', queueopts, msgprops, my_msg, msgid);END;

Declare records to hold various enqueue and msg properties.

Set up the payload with an object constructor.

Place the message on the specified queue and get a

msg ID in return.aqenq*.*

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 121

More Interesting Enqueue ExampleDECLARE ... Same setup as previous page ...BEGIN my_msg := message_type ( 'First Enqueue', 'May there be many more...');

msgprops.delay := 3 * 60 * 60 * 24; DBMS_AQ.ENQUEUE ('msgqueue', queueopts, msgprops, my_msg, msgid1);

my_msg := message_type ( 'Second Enqueue', 'And this one goes first...');

queueopts.sequence_deviation := DBMS_AQ.BEFORE; queueopts.relative_msgid := msgid1;

DBMS_AQ.ENQUEUE ( 'msgqueue', queueopts, msgprops, my_msg, msgid2);END;

Specify a delay before the payload

is available.

Modify the dequeue sequence by changing the deviation field and relative

msg ID.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 122

Dequeue ExampleDECLARE queueopts DBMS_AQ.DEQUEUE_OPTIONS_T; msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; msgid aq.msgid_type; /* defined in aq.pkg */ my_msg message_type;

PROCEDURE getmsg (mode_in IN INTEGER) IS BEGIN queueopts.dequeue_mode := mode_in;

DBMS_AQ.DEQUEUE ( 'msgqueue', queueopts, msgprops, my_msg, msgid); END;BEGIN getmsg (DBMS_AQ.BROWSE); getmsg (DBMS_AQ.REMOVE); getmsg (DBMS_AQ.REMOVE);END;

Declare records to hold various dequeue and msg properties.

Dequeue operation isolated in local

module.

Demonstrates destructive and non-destructive

dequeuing.aqdeq*.*

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 123

Prioritized Payloads You can assign priorities to individual payloads and then dequeue

according to those priorities.– The lower the numeric priority value, the higher the priority.

A stack implementation using AQ demonstrates this well.

PROCEDURE push (item IN VARCHAR2) IS queueopts DBMS_AQ.ENQUEUE_OPTIONS_T; msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; msgid aq.msgid_type; item_obj aqstk_objtype;BEGIN item_obj := aqstk_objtype (item); msgprops.priority := g_priority; queueopts.visibility := DBMS_AQ.IMMEDIATE; g_priority := g_priority - 1; DBMS_AQ.ENQUEUE ( c_queue, queueopts, msgprops, item_obj, msgid);END;

aqstk.pkgaqstk2.pkgpriority.*

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 124

Defining Message Subscribers You can specify that a message is to be enqueued for a list

of subscribers.– The message is then not removed from the queue until all

subscribers have dequeued the message.

Steps to working with a subscriber list:– 1. The queue table must be defined to support multiple subscribers

or consumers.

– 2. Add subscribers for the queue.

BEGIN DBMS_AQADM.CREATE_QUEUE_TABLE ( queue_table => 'major_qtable', queue_payload_type => 'student_major_t', multiple_consumers => TRUE);

DBMS_AQADM.ADD_SUBSCRIBER ( c_queue, SYS.AQ$_AGENT (name_in, NULL, NULL)); aqmult*.*

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 125

Oracle AQ - Summary Very powerful and flexible architecture.

– Much more robust that DBMS_PIPE.– Significant enhancements in Oracle8i, supporting a

publish-subscribe model, improved security, LISTEN capability.

Considered by Oracle to be a core component of its overall solution.– Crucial for Oracle to have a message-oriented

middleware in order to offer an all-Oracle solution.– Should be strongly supported "down the road".

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 126

Achieving PL/SQL Excellence

Managing Large Objects

with DBMS_LOB

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 127

LOB Terms

LOB = Large OBject: a category of datatype allowing storage of “unstructured” data up to 4 gigabytes

LOB datatype can be:– Column in table– Attribute in object type– Element in nested table

Possible applications include office documents, Images, sounds, video, etc.

LOB datatypes are the successor to LONGs.– Oracle has announced deprecation of LONGs; they should no

longer be used, though they will probably not be actually de-supported.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 128

Types of Large Objects “Internal” LOBs

– BLOB: unstructured binary data

– CLOB: single-byte fixed-width character data

– NCLOB: multi-byte fixed-width character data (or varying width in )

“External” LOBs

– BFILE: pointer to an operating system file

Key programming differences:

– Internal LOBs participate in transactions; external do not

– External LOBs are read-only from within Oracle

Temporary LOBs

– Internal LOBs that do not participate in transactions, improving performance.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 129

PL/SQL built-ins for LOBs Package DBMS_LOB

– Supports for all LOBs: Reading, substring and instring searches, comparison and length checking

– For internal LOBs: Write, append, copy, erase, trim– For external LOBs: File existence test, open, close

Other built-in functions– LOADFROMFILE (load an external BFILE into a BLOB)– EMPTY_LOB(), EMPTY_CLOB()– BFILENAME

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 130

BLOB-typed column in a table

Retrieve record with BLOB into PL/SQL variable

Deceptively simple example

CREATE TABLE incoming_faxes (fax_id INTEGER, received DATE, fax BLOB);

DECLARE CURSOR fax_cur IS SELECT fax FROM incoming_faxes; the_fax BLOB;BEGIN OPEN fax_cur; FETCH fax_cur INTO the_fax; CLOSE fax_cur;END;

Question:What’s in the_fax?

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 131

LOB columns [usually] contain pointers, not the LOBs themselves

Updating the LOB changes the pointer

This complicates use of LOBs– LOB locators cannot span transactions– Programs must lock records containing LOBs before updating– Programs performing DML must accommodate dynamic LOB locator

values

The “LOB locator”

fax_id received fax

281937 12-JAN-98 <lob locator>

<lob segment>

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 132

Example: Piecewise CLOB programmatic insert

Create a table

Must get a new LOB locator before writing into the LOB:

CREATE TABLE web_pages ( url VARCHAR2(512) PRIMARY KEY, htmlloc CLOB);

DECLARE the_url web_pages.url%TYPE := 'http://www.oodb.com'; the_loc CLOB;BEGIN INSERT INTO web_pages VALUES (the_url, EMPTY_CLOB()) RETURNING htmlloc INTO the_loc;

...

1 of 2

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 133

Example: Piecewise CLOB programmatic insertDECLARE the_url web_pages.url%TYPE := 'http://www.oodb.com'; the_loc CLOB; html_tab UTL_HTTP.HTML_PIECES; piece_length PLS_INTEGER; running_total PLS_INTEGER := 1;

BEGIN INSERT INTO web_pages VALUES (the_url, EMPTY_CLOB()) RETURNING htmlloc INTO the_loc;

html_tab := UTL_HTTP.REQUEST_PIECES(url => the_url);

FOR the_piece_no IN 1..html_tab.COUNT LOOP piece_length := LENGTH(html_tab(the_piece_no)); DBMS_LOB.WRITE(lob_loc => the_loc, amount => piece_length, offset => running_total, buffer => html_tab(the_piece_no)); running_total := running_total + piece_length; END LOOP;END;

This block retrieves and loads a web page into a CLOB column

Note: We are ignoring several likely exceptions

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 134

Example: Piecewise CLOB programmatic update

DECLARE CURSOR hcur IS SELECT htmlloc FROM web_pages WHERE url = 'http://www.oodb.com' FOR UPDATE; the_loc CLOB; str_offset INTEGER;BEGIN OPEN hcur; FETCH hcur INTO the_loc; CLOSE hcur;

str_offset := DBMS_LOB.INSTR(lob_loc => the_loc, pattern => 'oodb'); IF str_offset != 0 THEN DBMS_LOB.WRITE(lob_loc => the_loc, amount => 4, offset => str_offset, buffer => 'cool'); END IF;END;

Program must lock the record explicitly

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 135

Preparing to use BFILEs You must first create an Oracle “DIRECTORY”

– Creates logical alias for full directory path– Similar to LIBRARY (used for C "external procedures"), but

DIRECTORY namespace is global

Syntax:

Example: CREATE DIRECTORY web_pix AS 'D:\bill\training\pix';

CREATE OR REPLACE DIRECTORY <directory name> AS '<full path to directory>';

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 136

Using BFILE datatype in DDL In a table...

CREATE TABLE web_graphics ( image_id INTEGER, image BFILE);

In an object type...

CREATE TYPE Business_card_t AS OBJECT ( name Name_t, addresses Address_tab_t, phones Phone_tab_t, scanned_card_image BFILE);/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 137

The built-in BFILENAME function

Returns value of datatype BFILE. Spec:

Example:

Notes:– No automatic file checking or synchronization– No participation in transactions

DECLARE picture BFILE := BFILENAME('WEB_PIX', 'prodicon.gif');BEGIN INSERT INTO web_graphics VALUES (100015, picture);END;/

FUNCTION BFILENAME(directory_exprn, file_exprn) RETURN BFILE;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 138

Loading File into Database BLOB

DECLARE pic_file BFILE := BFILENAME('WEB_PIX', 'prodicon.gif'); pic_blob_loc BLOB := EMPTY_BLOB();

BEGIN INSERT INTO web_graphic_blobs VALUES (1, pic_blob_loc) RETURNING image INTO pic_blob_loc;

DBMS_LOB.FILEOPEN(pic_file, DBMS_LOB.FILE_READONLY);

DBMS_LOB.LOADFROMFILE(dest_lob => pic_blob_loc, src_lob => pic_file, amount => DBMS_LOB.GETLENGTH(pic_file));

DBMS_LOB.FILECLOSE(pic_file);END;/

CREATE TABLE web_graphic_blobs ( image_id INTEGER, image BLOB);

loadblob.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 139

New DBMS_LOB APIs in 8i Support for temporary LOBs

– No logging or rollback faster!– Lifespan: session, call, or transaction

Ability to retrieve LOB “chunk size”– Allows programmer to tune READs and WRITEs

For all internal LOBs– OPEN & CLOSE allow control over timing; that can mean

less I/O» Trigger will not fire until CLOSE» Indexes will not update until CLOSE

– ISOPEN

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 140

HOT

COLD

Large Object - Summary LOB support in Oracle is now significantly

improved.– Complete support in PL/SQL (and other APIs)– Much better performance and control than with

LONGs– Usable in object types

Some weaknesses...– LOB locator behavior slightly abstruse– Inability to modify BFILEs directly from within

PL/SQL.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 141

Achieving PL/SQL Excellence

Leveraging JavaInside PL/SQL

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 142

Overview of Java interoperability Java inside or outside 8i server can call PL/SQL

– Standard JDBC and SQLJ calls with Oracle extensions– Same Java on client, mid-tier, or server

Not covered in this seminar

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

But first...a BRIEF introduction to Java...

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 143

Question 1: What is Java? Could it be...

– The end of programming history as we know it?– The easiest, fastest, slickest piece of software ever designed by

human beings?– Just the latest in a series of "silver bullets" promoted by software

vendors in order to prop up quarterly sales?– The first and only successful O-O language?– None of the above?

We don't really need to take a vote.– We just need to keep a firm grip on common sense and stay focused

on delivering solutions.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 144

Question 2: Will Java Replace PL/SQL? While that scenario is certainly possible, it is very unlikely

and totally unthinkable for years to come.

PL/SQL will still be:– Faster and more productive than Java for database operations.– A language in which hundreds of thousands of developers are

trained.– Ubiquitous in thousands of production applications and millions of

lines of code.– Supported and improved by Oracle -- and very aggressively, to boot.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 145

Some Important Things to Remember Java is a case sensitive language...

– string is definitely not the same as String.

Everything is a class (or an object instantiated from a class)...– Before you can call a (non-static) class method, you have to

instantiate an object from that class.– Well, everything exception the primitive datatypes.

You don't have to know how to do everything with Java to get lots of value out of it...– Don't get overwhelmed by all the classes and all the strange quirks.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 146

Building a Java Class

public class Hello { public static void main (String[] args) { System.out.println ("Hello world!"); }}

CREATE OR REPLACE PROCEDURE hello ISBEGIN DBMS_OUTPUT.PUT_LINE ('Hello world!');END;

Oh, by the way: the PL/SQL

version

Let's start from the very beginning...

No members, no methods, except the "special" main method. – Used to test or run stand-alone a class,

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 147

Compiling Classes Before you can use a class, you must compile it with the

javac command. – You must also either have set the CLASSPATH or include it in your

javac call.– This will convert the .java file to a .class file.– It will also automatically recompile any classes used by your class

that has not been compiled since last change.

D:> javac Hello.java -classpath d:\java

SET CLASSPATH = d:\Oracle\Ora81\jdbc\lib\classes111.zip; e:\jdk1.1.7b\lib\classes.zip;d:\java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 148

Running a Class Run a class? What does that mean? It means that if your

class contains a method with this header:

public static void main (String[] args)

d:\java> java Hello

d:\java> java Hello mom

then you can "run" the main method with the java command:

You can also pass one or more arguments on the command line:

Hello2.java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 149

Using a Class The main method is handy for providing a built-in test

mechanism of your class.

Usually, however, you will use a class by instantiating objects from the class and then invoking class methods on the object.

Let's build a performance analyzer class to explore these ideas.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 150

Compare Performance of Methods Use System.currentTimeMillis to calculate elapsed time to

nearest thousandth of a second.

class Tmr { private long Gstart = 0;

public void capture () { Gstart = System.currentTimeMillis(); } public void showElapsed () { p.l ("Elapsed time ", System.currentTimeMillis() - Gstart); }

public long elapsed () { return (System.currentTimeMillis() - Gstart); }

public void showElapsed (String context) { p.l ("Elapsed time for " + context, elapsed()); } }

p.javaTmr.java

InFile.java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 151

Types of Classes There are several different kinds of classes in Java, besides

the "regular" kind we just saw...

Abstract class– The class can contain members and methods, but at least one

method remains unimplemented.

Interface class– The class only contains unimplemented methods.

Inner class– Classes that are defined inside other classes.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 152

And Now for Some Scary Buzzwords! Inheritance

– Subclasses inherit methods and variables from extended superclasses.

Polymorphism– An object of a class can have "multiple" forms, either as its own class or as

any superclass.

– Dynamic polymorphism: form is determined at run-time.

– Static polymorphism: form is chosen at compile-time (PL/SQL overloading).

A ClassHierarchy

Person.java

Person

Citizen

Corporation

War Criminal

Employee

supertype/"wider" subtype/"narrower"

Hourly Worker

Salaried Worker

Management

Non-Management

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 153

Language Basics Comments

Primitive datatypes– So I lied; these are not objects

instantiated from classes.

// Single line comment

/* Block comment */

/** Documentation comment */

booleancharbyteshort

intlongfloatdouble

Strings– A String object is a read-only; if you

assign a new value to it, you are actually allocating a new object.

– Can't do a direct == comparison.

String myName;myName = "Steven";myName = "Feuerstein";

if (myName.equals(YourName)) foundFamily();

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 154

Writing Loops in Java Three kinds of loops: while, do-while and for

– Note: you need squiggly brackets only if there is more than one statement in the loop body.

do { lostsaStuff} while (expression);

while (expression) { lostsaStuff}

for (initialize; expression; step) { lotsaStuff}

for (indx indx=0; indx < args.length; indx++) System.out.println (args[indx]);

static void processAll (Enumeration enum) { while (enum.hasMoreElements ()) { processIt (enum.NextElement()); System.out.println ( (String)enum.nextElement()) } }

Examples:

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 155

Passing Parameters Some important differences from PL/SQL...

– No default values for arguments; you must supply a value for each parameter.

– Only positional notation is supported.– If a method has no arguments, you still include the open and close

parentheses.

class PuterLingo { private String mname; public PuterLingo (String name) { mname = name; } public String name () { return mname; }}class LotsALingos { public static void main (String args[]) { PuterLingo Java = new PuterLingo("Java"); System.out.println (Java.name()); }}

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 156

Exception Handling in Java Very similar to PL/SQL; you "throw" and "catch" exceptions,

rather than raise and handle.

public static int numBytes (String filename) { try { if (filename.equals("")) throw new Exception ("Filename is NULL");

File myFile = new File (filename); return myFile.length(); } catch (SecurityException e) { return -1; } catch (Exception e) { System.out.println (e.toString()); }}

"WHEN OTHERS" in Java

File-related exceptions

Throwing my own exception

Put inside try clause

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 157

Differences Between Java & PL/SQL Exceptions are objects derived directly or indirectly from the

Exception class.– So you can pass them as arguments and do anything and everything

else you can do with objects.

You must inform users of your method of which exceptions may be thrown.– Use the throws clause in your specification, as in:

public static int numBytes (String filename) throws SecurityException, NoSuchFile { ...}

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 158

Java's Not So Tough! You can learn enough Java in less than a week to:

– Build simple classes– Leverage Java inside PL/SQL

Moving to the next level of expertise will be more of a challenge.– Object oriented development (Java) is very different from procedural

coding (PL/SQL).

Now let's explore how you can put Java to work for you inside PL/SQL programs.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 159

Java Stored Procedures (JSPs)

Oracle 8i server

Java virtual machine running Java method

PL/SQL cover for Java method

Java applet or app. using JDBC or

SQLJ

Oracle Developer

client (PL/SQL)

OCI or Pro*C client

VB or C++ via OO4O or

ODBC

Net8

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 160

JSPs: Some sample usesPL/SQL extender

– For example, better file I/O– RMI callouts, network communication in/out

PL/SQL replacement– More standard language– Good performer for numeric processing tasks– Beware database I/O & string manipulation performance

Scaleable deployment of third party code– Vertical application– Web server– XML parser/generator

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 161

Creating JSP to call from PL/SQL 1. Create Java classes in your favorite IDE

2. Load into server using “loadjava” command-line tool

3. Publish PL/SQL cover using AS LANGUAGE JAVA... rather than BEGIN...END

4. Grant privileges as desired

5. Call from PL/SQL (or SQL) as if calling PL/SQL

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 162

Create Java class(es)

toString method automatically used by System.out.println

main method is used to test the class.

Entry points must be public static in most cases

Classes may call other classes

Avoid GUI calls

class Corporation extends Person { long layoffs; long CEOCompensation;

public Corporation ( String Pname, long Playoffs, long PceoComp) { name = Pname; layoffs = Playoffs; CEOCompensation = PceoComp; } public String toString () { return name + " is a transnational entity with " + layoffs + " laid-off employees, paying its" + " Chief Executive Officer " + CEOCompensation; } public static void main (String[] args) { // A very scary company Corporation TheGlobalMonster = new Corporation ( "Northrup-Ford-Mattel-Yahoo-ATT", 5000000, 50000000);

System.out.println (TheGlobalMonster); }}

Notes on Java classes

person.java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 163

Upload using “loadjava” utility

Example:

loadjava options (abbreviated)-oci8 loadjava will connect using OCI driver

-resolve Resolves external class references atcompile time

-resolver (shown later) Search path like CLASSPATH

loadjava -user scott/tiger -oci8 -resolve datacraft/bill/Hello.class

Oracle 8i server

loadjavaJava class

Java source

Java resource

.class fileJava

resource file

.jar file.java file

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 164

Publish

Example (top-level call spec)

Syntax (simplified)

CREATE OR REPLACE FUNCTION hello_emp (empno_in IN NUMBER)RETURN VARCHAR2AS LANGUAGE JAVA NAME 'datacraft.bill.Hello.Emp(int) return java.lang.String';/

CREATE [ OR REPLACE ] { PROCEDURE | FUNCTION } <name> [ RETURN <sqltype> ][ ( <args> ) ][ AUTHID { DEFINER | CURRENT_USER } ]AS LANGUAGE JAVANAME '<method fullname> (<Java type fullname>, ...)[ return <Java type fullname> ]';

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 165

Call the wrapped method Method 1: Call as if PL/SQL module

Method 2: Use 8i SQL CALL statement; for example, from SQL*Plus:

– CALL avoids overhead of SELECT fn FROM DUAL

BEGIN DBMS_OUTPUT.PUT_LINE(hello_emp(7499));END;

VARIABLE thename VARCHAR2(12)CALL hello_emp(7499) INTO :thename;PRINT :thename

jsp.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 166

Publishing -- more conceptsShape mapping

– Java methods declared “void” become PL/SQL procedures

– Signature mismatches detected only at runtime

Type mapping (typical)java.lang.String VARCHAR2

java.sql.Timestamp DATE

java.math.BigDecimal NUMBER

oracle.sql.STRUCT user-defined object type

<named type> user-defined object type

oracle.sql.REF object REFerence

oracle.sql.ARRAY user-defined collection type

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 167

Publish in PL/SQL Package Spec Designate Java module in PL/SQL package spec...

CREATE OR REPLACE PACKAGE hello_pkgAS FUNCTION hi_emp (empno_in IN NUMBER) RETURN VARCHAR2 AS LANGUAGE JAVA NAME 'datacraft.util.Hello.Emp(int) return java.lang.String';END;/

(No package body required in this case)

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 168

Publish as module in package body ...or in package body

CREATE OR REPLACE PACKAGE hello_pkg2AS FUNCTION hi_emp (empno_in IN NUMBER) RETURN VARCHAR2;END;/

CREATE OR REPLACE PACKAGE BODY hello_pkg2AS FUNCTION hi_emp (empno_in IN NUMBER) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'datacraft.util.Hello.Emp(int) return java.lang.String';END;/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 169

Publish as object type method Either in

spec:CREATE OR REPLACE TYPE foo_t AS OBJECT ( bar VARCHAR2(30), MEMBER FUNCTION hello_emp RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'datacraft.util.Hello.Emp(int) return java.lang.String');/

CREATE OR REPLACE TYPE foo_t AS OBJECT ( bar VARCHAR2(30), MEMBER FUNCTION hello_emp RETURN VARCHAR2);/

CREATE OR REPLACE TYPE BODY foo_t AS MEMBER FUNCTION hello_emp RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'datacraft.util.Hello.Emp(int) return java.lang.String';END;/

or in thebody:

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 170

New DDL Statements and Roles CREATE JAVA

– Alternative to “loadjava” utility– Creates or replaces an Oracle “library unit” from Java source, class,

or resource– Can read file designated with BFILE() function

ALTER JAVA: compiles Java source, resolves Java class references.

DROP JAVA: drops a named Java library unit

Several roles available for Java operations:– JAVAUSERPRIV, JAVASYSPRIV (needed for file IO operations),

JAVA_ADMIN, JAVAIDPRIV, JAVADEBUGPRIV– You can also grant specific privileges.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 171

Passing Oracle8 objects

Harder than you’d think

Three different interfacing techniques– oracle.sql.STRUCT– oracle.sql.CustomDatum– oracle.jdbc2.SQLData

Oracle8i server

Object in O-R table

Java applicationJava application

Java object

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 172

JPublisher utility

What does it do?– Generates Java that

encapsulates type and REF

Three types of mapping supported for methods

– “Oracle mapping”– “JDBC mapping”– “Object JDBC mapping”

Database server

Definition of type or REF in data dictionary

JPublisher

Generated Java file(s) for use in Java programs

User-supplied JPublisher input file

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 173

Passing object using JPub-generated Java

After generating and uploading classes with JPub, they become available to use in mapping

Example of passing an Account_t object:

CREATE OR REPLACE PROCEDURE account_save (new_acct IN Account_t)IS LANGUAGE JAVA NAME 'datacraft.bill.AccountRuntime.save (datacraft.bill.Account_t)';/

jspobj.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 174

Example: Improving File I/O

You can read/write files in PL/SQL with UTL_FILE, but that package is very limited.

Java offers many file-related classes with much greater capabilities.

Let's see how we can make that great Java stuff available from within PL/SQL.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 175

Encapsulate Java Classes You won't generally access native Java methods in

your PL/SQL wrapper.– Instead build a static method that instantiates a Java

object from the class and then invokes the relevant method against that object.

Let's start with something simple...– The File class offers a length method that returns the

number of bytes in a file. – This is not available through UTL_FILE (though you can

get it through DBMS_LOB).

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 176

My Own Java Class for File Manipulation

Accept the name of a file and return its length.

import java.io.File;public class JFile2 { public static long length (String fileName) { File myFile = new File (fileName); return myFile.length(); }}

Take each of these steps:– Import the File class to resolve reference.– Instantiate a File object for the specified name.– Call the method of choice against that object and return

the value.

JFile2.java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 177

Build a Package over Java Method Let's put it in a package; we will certainly want to

add more functionality over time.– I translate the Java long to a PL/SQL NUMBER.

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 178

Mapping the Boolean Datatype Both Java and PL/SQL support a native Boolean

datatype, so you'd expect smooth sailing. Not so!

To pass a Boolean back from Java to PL/SQL, you will need to take these steps:– 1. Convert the Java boolean to a String or number and

return that value.– 2. Write a "hidden" PL/SQL wrapper function that returns

the string or number.– 3. Write a "public" PL/SQL wrapper function to convert

that number to a true PL/SQL Boolean.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 179

Translate Boolean to Number Accept the name of a file and return its length.

import java.io.File;

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

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

JFile3.java

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 180

Wrapper for Pseudo-Boolean function Simple translation back to PL/SQL Boolean.

– Avoid the hard-codings with named constants...

CREATE OR REPLACE PACKAGE xfile IS FUNCTION canRead (file IN VARCHAR2) RETURN BOOLEAN;END;/CREATE OR REPLACE PACKAGE BODY xfileIS FUNCTION IcanRead (file IN VARCHAR2) RETURN NUMBER AS LANGUAGE JAVA NAME 'JFile3.canRead (java.lang.String) return int';

FUNCTION canRead (file IN VARCHAR2) RETURN BOOLEAN AS BEGIN RETURN IcanRead (file) = 1; END;END;/

xfile3.pkgJFile4.javaxfile4.pkgJFile.javaxfile.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 181

Passing Objects to Java with STRUCT You can pass Oracle object information to Java without relying on JPub by using the

STRUCT class.

public class UnionBuster {

public static void wageStrategy (STRUCT e) throws java.sql.SQLException { // Get the attributes of the labor_source object. Object[] attribs = e.getAttributes();

// Access individual attributes by array index, // starting with 0 String laborType = (String)(attribs[0]); BigDecimal hourly_rate = (BigDecimal)(attribs[1]); System.out.println ( "Pay " + laborType + " $" + hourly_rate + " per hour"); }}

Obtain attributes of the Oracle object.

Extract individual attribute values from

the array.UnionBuster.java

passobj.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 182

CREATE OR REPLACE PROCEDURE bust_em_with ( labor_source_in IN labor_source_t) AS LANGUAGE JAVA NAME 'UnionBuster.wageStrategy (oracle.sql.STRUCT)';/

BEGIN bust_em_with ( labor_source ('Workfare', 0)); bust_em_with ( labor_source ('Prisoners', '5'));END;

Wrapping a STRUCT-based Method You can pass Oracle object information to Java without relying on JPub by using the

STRUCT class.

CREATE TYPE labor_source_t AS OBJECT ( labor_type VARCHAR2(30), hourly_rate NUMBER);/

The Oracle object type definition

Fully specify the STRUCT class in

parameter list.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 183

Viewing Output from Java Methods Java provides a "print line" method similar to

DBMS_OUTPUT.PUT_LINE: System.out.println.– Call it within methods and output will display in your Java

environment.

When called within a PL/SQL wrapper, you can redirect the output to the DBMS_OUTPUT buffer. – Here is a good nucleus for a login.sql file:

public class HelloAll { public static void lotsaText ( int count) { for (int i = 0; i < count; i++) { System.out.println ( "Hello Hello Hello Hello Hello All!"); }}}

SET SERVEROUTPUT ON SIZE 1000000CALL DBMS_JAVA.SET_OUTPUT (1000000);

HelloAll.javaHelloAll.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 184

Error Handling with Java-PL/SQL Java offers a very similar, but more robust error handling

mechanism than PL/SQL.– Exceptions are objects instantiated from the Exception class or a

subclass of it, such as java.sql.SQLException.– Instead of raising and handling, you "throw" and "catch".– Use two methods, getErrorCode() and getMessage() to obtain

information about the error thrown.

Any error not caught by the JVM (Java virtual machine) will be thrown back to the PL/SQL block or SQL statement.– And also spew out the entire Java error stack! (at least through

8.1.5).

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 185

Trapping and Identifying Errors Currently, the entire Java stack is displayed on your screen, and you have

to do some digging to extract the Oracle error information.

SQL> BEGIN 2 dropany ('TABLE', 'blip'); 3 EXCEPTION 4 WHEN OTHERS 5 THEN 6 DBMS_OUTPUT.PUT_LINE (SQLCODE); 7 DBMS_OUTPUT.PUT_LINE (SQLERRM); 8 END; 9 /java.sql.SQLException: ORA-00942: table or view does not exist at oracle.jdbc.kprb.KprbDBAccess.check_error(KprbDBAccess.java) at oracle.jdbc.kprb.KprbDBAccess.parseExecuteFetch(KprbDBAccess.java) at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecuteWithBatch(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecute(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.executeUpdate(OracleStatement.java) at DropAny.object(DropAny.java:14)-29532ORA-29532: Java call terminated by uncaught Java exception: java.sql.SQLException: ORA-00942: table or view does not exist

DropAny.javadropany.tst

getErrInfo.spdropany2.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 186

Achieving PL/SQL Excellence

External Procedures

An external procedure is a 3GL routine that can serve as the “body” of a PL/SQL function, procedure, or method.

– Must be a shared object library on Unix or a dynamically linked library (DLL) on Windows.

An Oracle8 feature that allowed relatively "native" callouts to C from PL/SQL for the first time.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 187

External Shared Library

Application Net8

PL/SQL Runtime Engine

Process & data flow

Client or server-side application

Client or server-side application

PL/SQL body

PL/SQL body

External Procedure Listener

External Procedure Listener

extprocextproc

Routine in .DLL or .so

file

Routine in .DLL or .so

file

Calls

Makes request

spawns

calls routine from

returns resultsreturns

results

returns results

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 188

External procedures: Sample uses Send email

Invoke operating system command

Invoke custom or legacy application

Call C runtime library function

Perform admin tasks

email.sql

diskspace.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 189

Determine free disk space on NT (1/4) Use pre-existing routine:

– GetDiskFreeSpaceA in kernel32.dll– For given drive letter, this function returns:

» sectors per cluster

» bytes per sector

» number of free clusters

» total number of clusters

» “return code” indicating success or failure

First, create an Oracle8 “library”:

CREATE OR REPLACE LIBRARY nt_kernel AS 'c:\winnt\system32\kernel32.dll';/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 190

Package spec (2/4) Nothing unexpected here:

CREATE OR REPLACE PACKAGE disk_utilAS FUNCTION get_disk_free_space (root_path IN VARCHAR2, sectors_per_cluster OUT PLS_INTEGER, bytes_per_sector OUT PLS_INTEGER, number_of_free_clusters OUT PLS_INTEGER, total_number_of_clusters OUT PLS_INTEGER) RETURN PLS_INTEGER;

END disk_util;/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 191

CREATE OR REPLACE PACKAGE BODY disk_util AS FUNCTION get_disk_free_space (root_path IN VARCHAR2, sectors_per_cluster OUT PLS_INTEGER, bytes_per_sector OUT PLS_INTEGER, number_of_free_clusters OUT pls_integer, total_number_of_clusters OUT PLS_INTEGER) RETURN PLS_INTEGER IS EXTERNAL LIBRARY nt_kernel NAME "GetDiskFreeSpaceA" LANGUAGE C CALLING STANDARD PASCAL PARAMETERS (root_path STRING, sectors_per_cluster BY REFERENCE LONG, bytes_per_sector BY REFERENCE LONG, number_of_free_clusters BY REFERENCE LONG, total_number_of_clusters BY REFERENCE LONG, RETURN LONG);END disk_util;

Package body in Oracle 8.0 (3/4)

All the magic is in the EXTERNAL section

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 192

Usage (4/4)

DECLARE lroot_path VARCHAR2(3) := 'C:\'; lsectors_per_cluster PLS_INTEGER; lbytes_per_sector PLS_INTEGER; lnumber_of_free_clusters PLS_INTEGER; ltotal_number_of_clusters PLS_INTEGER; return_code PLS_INTEGER; free_meg REAL;BEGIN return_code := disk_util.get_disk_free_space (lroot_path, lsectors_per_cluster, lbytes_per_sector, lnumber_of_free_clusters, ltotal_number_of_clusters);

free_meg := lsectors_per_cluster * lbytes_per_sector * lnumber_of_free_clusters / 1024 / 1024;

DBMS_OUTPUT.PUT_LINE('free disk space, Mb = ' || free_meg);END; diskspace.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 193

Creating your own external procedure 1. Identify or create shared library

2. Identify or CREATE LIBRARY within Oracle

3. Map the parameters using:

– EXTERNAL clause (Oracle8)or

– LANGUAGE clause (Oracle8i)

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 194

1. Identify/create external routine Prerequisite: O/S must support shared libraries

Some useful routines are pre-built in C runtime library– On Unix: /lib/libc.so– On NT: <systemroot>\system32\crtdll.dll

Building a shared library of your own requires knowledge of:– Appropriate language (typically C)– Compiler & linker

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 195

Syntax:

Assigns a programmer-defined alias to a specific shared library file

Notes– Requires CREATE LIBRARY privilege– Does not validate directory– Can’t use symbolic link

2. Create the Oracle library

CREATE OR REPLACE LIBRARY <library name> AS '<full path to file>';

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 196

3. Map the parameters

– Must pass extra parameters to designate:» NULL/NOT NULL state» String length» Maximum allocated length

– Six choices of parameter mode» IN» RETURN» IN BY REFERENCE» RETURN BY REFERENCE» OUT» IN OUT

This can get complicated, because…

These details are better suited for close reading…

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 197

Achieving PL/SQL Excellence

Oracle8i New Features

Autonomous TransactionsInvoker Rights ModelRow Level Security

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 198

Oracle8i New Features Oracle8i offers a number of PL/SQL-specific features that

give you tremendous additional flexibility and capability.– And the learning curve to take advantage of these features is

generally not too steep.

Autonomous Transactions

The Invoker Rights Model

Row level security

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 199

Autonomous Transactions Prior to Oracle8i, a COMMIT or ROLLBACK in any program

in your session committed or rolled back all changes in your session.– There was only one transaction allowed per connection.

With Oracle8i, you can now define a PL/SQL block to execute as an "autonomous transaction".– Any changes made within that block can be saved or reversed

without affecting the outer or main transaction.

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 200

When to Use Autonomous Transactions Reusable Application Components

– ATs are more or less required in the new distributed application architecture of the Internet.

– One component should not have any impact (esp. something like a COMMIT) on other components.

Logging Mechanism– Commonly developers lot to database tables, which can cause all sorts

of complications: your log entry becomes a part of your transaction.– Now you can avoid the complexities (need for ROLLBACK TO savepoints

and so on).

Tracing Code Usage – Build retry mechanisms, software usage meter, etc.

Call functions within SQL that change the database.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 201

Logging with ATs Don't forget that ROLLBACK in the exception section!

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;

While we're at it, let's add some session

information.

Avoid inter-dependencies with

the main transaction.

retry.pkgretry.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 202

Tips and Gotchas with ATs An AT program must COMMIT or ROLLBACK before terminating, or an

error is raised.

The AT PRAGMA can be used only with individual programs and top-level anonymous blocks.– You cannot define an entire package as an AT.

– You cannot define a nested anonymous block to be an AT.

The AT PRAGMA goes in the body of packages.– You cannot tell by looking at the package spec if you are calling ATs or not --

and this info is not available in the data dictionary.

Any changes committed in an AT are visible in the outer transaction.– You can use the SET TRANSACTION ISOLATION LEVEL SERIALIZABLE to

indicate that you do not want the changes visible until the outer transaction commits.

– Place the SET TRANSACTION statement in the outer transaction.

autonserial.sqlauton_in_sql.sqlautontrigger*.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 203

The Invoker Rights Model Prior to Oracle8i, whenever you executed a stored program,

it ran under the privileges of the account in which the program was defined.– This is called the …

With Oracle8i, you can now decide at compilation time whether your program or package will execute in the definer's schema (the default) or the schema of the invoker of the code.– This is called the …

Definer Rights Model

Invoker Rights Model

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 204

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.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 205

Problems with Definer Rights Deployment & maintenance

– Must install module in all remote databases where needed– In some databases, each user has own copy of table(s), requiring

copy of stored module

Security– No declarative way to restrict privileges on certain modules in a

package -- it's all or nothing, unless you write code in the package to essentially recreate roles programmatically.

– Can bypass 8i’s “fine-grained” access features (see the DBMS_RLS package)

– Difficult to audit privileges

Sure would be nice to have a choice...and now you do!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 206

For top level modules:

For modules with separate spec and body, AUTHID goes only in spec, and must be at the package level.

Synonyms may be necessary if modules use object names not qualified by schema.– In other words, do what it takes to get the code to compile. You could

also create local, "dummy" objects.– At run-time, the objects referenced may well be different from those

against which it compiled.

Oracle8i Invoker Rights Syntax

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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 207

"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

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 208

Tips and Gotchas for Invoker Rights Does not apply to code objects, only data

– When your stored code references another stored code element, the definer rights model is always applied to resolve the reference.

– Both static and dynamic SQL is supported.

Once a definer rights program is called, all other calls in stack are resolved according to definer rights.– AUTHID CURRENT_USER is ignored.

Information about the rights model is not available in the data dictionary

What if you want to maintain a single version of your code for both pre-Oracle8i and Oracle8i installations, taking advantage of the invoker rights model whenever possible?– A creative use of SQL*Plus substitution variables comes in very

handy. Note: cannot use with wrapped code. invdefinv.sqloneversion.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 209

You want to reuse the same code among many users, but you want their own directly-granted privileges to determine access.

Especially handy with dynamic SQL.– Share a program that uses dynamic SQL (whether DBMS_SQL or the

new native implementation) and you end up taking the most unexpected DDL and DML detours.

When to Invoke Invoker Rights

authid.sqlwhichsch*.sql

stolenlives

ChicagoSchema

stolenlives

New YorkSchema

Check City Statistics

National HQ

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 210

Combining Invoker & Definer Rights Rely on invoker rights to allow centralized code to work with

schema-specific data.

Rely on definer rights to access centralized data from any schema.

stolenlives

Chicago

stolenlives

New YorkCheck City Statistics

HQ

perpetrators

Analyze Pattern

perp.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 211

DBMS_RLSRow-LevelSecurity

Row Level Security

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 212

Row-Level Security with DBMS_RLS Oracle8i offers a new package, DBMS_RLS, with which to

implement automated row-level security (also referred to as "fine grained access control").

The establishment of security policies on (restricted access to) individual rows of a table.

Prior to Oracle8i, you could achieve this only partially through the use of views.

The DBMS_RLS package (along with "system contexts") now allow you to do so in a foolproof manner.

Row Level Security

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 213

Components Needed for RLS System Contexts

– A new feature in Oracle8i, the system context is a named set of attribute-value pairs global to your session.

Security Policy Packages– You will need to write a package containing functions that establish the

security policies to be applied to a particular table.

Database Logon Triggers– I want to make sure that when a person logs in, their context information is

set properly.

DBMS_RLS– Use programs in DBMS_RLS to associate your security policies with tables.

Let's step through a simple example to see how all these pieces tie together.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 214

A National Health Care System The year is 2010.

– A massive, popular uprising in the United States has forced the establishment of a national healthcare system. No more for-profit hospitals pulling billions of dollars out of the system; no more private insurance companies soaking up 30 cents on the dollar; all children are vaccinated; all pregnant women receive excellent pre-natal care.

We need a top-notch, highly secure database for NHCS. The main tables are patient, doctor, clinic and regulator.

Here are some rules:– Doctors can only see patients who are assigned to their clinic.– Regulators can only see patients who reside in the same state. – Patients can only see information about themselves.

fgac.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 215

Set the Context Define a context in the database, and create a procedure

that will set the context attributes (type and ID) upon login.

CREATE CONTEXT patient_restriction USING nhc_pkg;

PROCEDURE set_contextIS CURSOR doc_cur IS SELECT doctor_id FROM doctor WHERE schema_name = USER; doc_rec doc_cur%ROWTYPE;BEGIN OPEN doc_cur; FETCH doc_cur INTO doc_rec; DBMS_SESSION.SET_CONTEXT ( 'patient_restriction', c_person_type_attr, 'DOCTOR'); DBMS_SESSION.SET_CONTEXT ( 'patient_restriction', c_person_id_attr, doc_rec.doctor_id);END;

This is a simplification. See fgac.sql for logic that identifies

different types of people and sets the context accordingly.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 216

Define the Predicate A predicate is a string that will be appended to the WHERE

clause of the table to which this security policy is associated (see next page).

FUNCTION person_predicate ( schema_in VARCHAR2, name_in VARCHAR2) RETURN VARCHAR2 IS l_context VARCHAR2(100) := SYS_CONTEXT (c_context, c_person_type_attr); retval VARCHAR2(2000);BEGIN IF l_context = 'DOCTOR' THEN retval := 'home_clinic_id IN (SELECT home_clinic_id FROM doctor WHERE doctor_id = SYS_CONTEXT (''' || c_context || ''', ''' || c_person_id_attr || '''))';

We need a different string to modify the WHERE clause for each type of

person.

Extract the context information for this

connection.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 217

Create the Security Policy Use the DBMS_RLS.ADD_POLICY procedure.

– The following procedure call specifies that the WHERE clause of any query, update or delete against the SCOTT.PATIENT table will be modified by the string returned by the person_predicate function of the SCOTT.nhc_pkg package.

BEGIN DBMS_RLS.ADD_POLICY ( OBJECT_SCHEMA => 'SCOTT', OBJECT_NAME => 'patient', POLICY_NAME => 'patient_privacy', FUNCTION_SCHEMA => 'SCOTT', POLICY_FUNCTION => 'nhc_pkg.person_predicate', STATEMENT_TYPES => 'SELECT,UPDATE,DELETE');END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 218

Create Logon Trigger, Setting Context By setting the context in the logon trigger, we guarantee that the

context is set (and the predicate applied) no matter which product is the entry point to Oracle.

CREATE OR REPLACE TRIGGER set_id_on_logon AFTER LOGON ON DATABASEBEGIN nhc_pkg.set_context;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ( 'Error ' || SQLCODE || ' setting context for ' || USER');END;

Exception handling in trigger is critical! If you allow an exception to go

unhandled, logon is disabled.

fgac.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 219

Queries Transparently Filtered What you see is determined automatically by who you are.

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

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

Context Information for "CSILVA": Type: PATIENT ID: Predicate: schema_name = 'CSILVA'

Patients Visible to "CSILVA":CSILVA - Chris Silva - IL

Doctor sees only her patients.

Patient sees only himself.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 220

Take Full Advantage of PL/SQL!

It's so easy to fall into a rut with a programming language.

– You learn what you need to get the job done, and you use only what you know.

January... February... March... May...April...

Jump out of your rut - and

play a new tune with PL/SQL!

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 221

Appendices(a.k.a., If time permits…)

Achieving PL/SQL Excellence

UTL_FILE – file IO

DBMS_JOB – Job scheduling

DBMS_PIPE – Pipe-based communication

DBMS_UTILITY – the kitchen sink package

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 222

UTL_FILEServer-side

File I/O

Built-in Packages: UTL_FILE

FOPEN

GET_LINE

PUT_LINE

. . .

Application

Physical Files

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 223

UTL_FILE: Server-side I/O Allows you to read from and write to operating system

files on the database server.

A "version 1" (fairly primitive) utility...– Maximum of 1023 bytes per line (read or write) until Oracle 8.0.5,

when it jumps to 32K.– No higher-level file operations supported (change privileges,

delete, copy, random access to contents).– Limitations on files you can access (no mapped files, no use of

environmental variables).

But you can read lines from a file and write lines to a file.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 224

UTL_FILE Module Outline

FCLOSE Close the specified file

FCLOSE_ALL Close all open files in your session

FFLUSH Flush all data from the UTL_FILE buffer to your file

FOPEN Open a file

GET_LINE Get the next line from a file

IS_OPEN Returns TRUE if the file is open (sort of)

NEW_LINE Insert a new line character in file at the end of current line

PUT Puts text into the UTL_FILE buffer

PUT_LINE Puts text and new line character into UTL_FILE buffer.

PUTF Puts formatted text into the buffer

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 225

Authorizing Directory Access

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.

Follow these rules to avoid many headaches.– Use a separate entry for each directory (and subdirectory; there is no

subdirectory recursion).– No single or double quotes around directory, no trailing delimiter.

utl_file_dir = /tmputl_file_dir = /accounts/newdev

And don't do either of the following:utl_file_dir = .

Allows read & write for current

directory.

utl_file_dir = *

Allows read & write for any

directory.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 226

Test UTL_FILE Access About the hardest part to working with UTL_FILE is simply

getting started.

So before you write anything fancy, modify your initialization file, restart your database, and then run the following test script (it can't get much simpler than this):

DECLARE fid UTL_FILE.FILE_TYPE;BEGIN /* Change the directory name to one to which you at least || THINK you have read/write access. */ fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'W'); UTL_FILE.PUT_LINE (fid, 'hello'); UTL_FILE.FCLOSE (fid);END;/

utlfile.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 227

Opening a File

Specify file location, name and operation type.– Types are 'R' for Read, 'W' for Write and 'A' for Append.

The FOPEN function returns a record ("file handle") based on the UTL_FILE.FILE_TYPE.– Currently contains a single ID field.

Maximum of 10 files may be opened in each user session.

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');END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 228

Reading from a File

Can only read from a file opened with the "R" mode.

Lines in the file cannot be longer than 1023 bytes in length.– In Oracle8 Release 8.0.5 and above, the ceiling is raised to 32K.

The NO_DATA_FOUND exception is raised if you read past the end of the file.– You might want to build your own GET_LINE which handles the

exception and returns an EOF Boolean status flag.

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;

getnext.sp

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 229

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.– The file buffers are automatically flushed when you close a file or exit

your session.

DECLARE fid UTL_FILE.FILE_TYPE;BEGIN fid := UTL_FILE.FOPEN ('c:\temp', 'test.txt', 'W'); UTL_FILE.PUT_LINE (fid, 'UTL_FILE'); UTL_FILE.PUT (fid, 'is so much fun'); UTL_FILE.PUTF (fid, ' that I never\nwant to %s', '&1'); UTL_FILE.FCLOSE (fid);END;

UTL_FILEis so much fun that I neverwant to stop

Resulting Text

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 230

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;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 231

Error Handling in UTL_FILE

UTL_FILE relies on a combination of user-defined exceptions and STANDARD exceptions to communicate errors.– NO_DATA_FOUND when you try to read past the end of the file.– UTL_FILE-named exceptions in other cases.

You have to take special care to trap and handle the named exceptions.– They all share a common SQLCODE of 1.

PACKAGE UTL_FILE IS invalid_path EXCEPTION; invalid_mode EXCEPTION; invalid_filehandle EXCEPTION; invalid_operation EXCEPTION; read_error EXCEPTION; write_error EXCEPTION; internal_error EXCEPTION;END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 232

Recommended Exception Section

Trap locally by name; record the error, translating the generic user-defined exception into an understandable message.

Re-raise exception if you want it to propagate from that block.

EXCEPTION WHEN UTL_FILE.INVALID_PATH THEN recNgo (PLVfile.c_invalid_path); RAISE; WHEN UTL_FILE.INVALID_MODE THEN recNgo (PLVfile.c_invalid_mode); RAISE; WHEN UTL_FILE.INVALID_FILEHANDLE THEN recNgo (PLVfile.c_invalid_filehandle); RAISE; WHEN UTL_FILE.INVALID_OPERATION THEN recNgo (PLVfile.c_invalid_operation); RAISE; WHEN UTL_FILE.READ_ERROR THEN recNgo (PLVfile.c_read_error); RAISE; WHEN UTL_FILE.WRITE_ERROR THEN recNgo (PLVfile.c_write_error); RAISE; WHEN UTL_FILE.INTERNAL_ERROR THEN recNgo (PLVfile.c_internal_error); RAISE;END;

utlflexc.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 233

DBMS_JOBScheduled Executionof Stored Procedures

Built-in Packages: DBMS_JOB

SUBMIT

RUN

REMOVE

. . .

ApplicationOracle Job QueueSubsystem

BackgroundProcess

DBA_JOBS

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 234

Overview of DBMS_JOB DBMS_JOB provides an API to the Oracle job queues, which in

turn offers job scheduling capabilities within the Oracle Server.

Built by Oracle to support snapshots and replication.

Made its debut in PL/SQL Release 2.1, but only “publicly available” and supported in PL/SQL Release 2.2.

You can use DBMS_JOB to:– Replicate data between different database instances.– Schedule regular maintenance on instances.– Schedule large batch jobs to run on "off hours".– Create a listener program to poll the contents of a pipe and take action.– Spawn background processes to avoid blocking client process.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 235

DBMS_JOB Module Outline

BROKEN Marks the job as either FIXED or BROKEN. Broken jobs will not run as scheduled.

CHANGE Changes one or all attributes of a job.

INTERVAL Changes the interval between executions of a job.

ISUBMIT Submits a job to the queue using a predefined job number.

NEXT_DATE Changes when a queued job will run.

REMOVE Removes the job from the queue.

RUN Forces immediate execution of the specified job number.

SUBMIT Submits a job to the queue returning a unique job number.

USER_EXPORT Returns the job string for a job number.

WHAT Changes the job string of a job.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 236

Submitting a Job

A job is a call to a stored procedure or an anonymous block– It must end with a semi-colon and can contain “hard-coded” arguments.

When you submit a job, you specify the date on which it should next execute, and then the job’s execution interval (frequency of execution).– In the above example, I run calculate_totals immediately and then on a daily basis thereafter. Notice that the start

time is a DATE expression, while the interval is a string (this is dynamic PL/SQL!)

You can also call DBMS_JOB.ISUBMIT and supply the job number, instead of having DBMS_JOB generate one for you.

DECLARE job# BINARY_INTEGER;BEGIN DBMS_JOB.SUBMIT (job#, 'calculate_totals;', SYSDATE, 'SYSDATE + 1');END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 237

Submit Job Example

This block submits a job that uses a built-in procedure, DBMS_DDL.ANALYZE_OBJECT, to analyze a specific table every evening at midnight.

DECLARE v_jobno INTEGER;

BEGIN DBMS_JOB.submit ( job => v_jobno, what => 'DBMS_DDL.ANALYZE_OBJECT ' || '(''TABLE'',''LOAD1'',''TENK''' || ',''ESTIMATE'',null,estimate_percent=>50);', next_date => TRUNC (SYSDATE + 1), interval => 'TRUNC(SYSDATE+1)' );

p.l (v_jobno);

END;/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 238

DBMS_JOB.ISubmit Job Example

This block submits three jobs to the job queue, numbered 1,2, and 3. – Job 1 passes a string and number into procedure MY_JOB1, runs it in one hour and executes every day

thereafter.– Job 2 passes a date into procedure MY_JOB2, executes for the first time tomorrow and every 10 minutes

thereafter. – Job 3 is a PL/SQL block which does nothing, executes immediately, and will be removed from the queue

automatically.

BEGIN DBMS_JOB.ISUBMIT (job => 1 ,what => 'my_job1(''string_parm_value'',120);' ,next_date => SYSDATE + 1/24 ,interval => 'SYSDATE +1');

DBMS_JOB.ISUBMIT (2, 'my_job2(date_IN=>SYSDATE);' ,SYSDATE+1,'SYSDATE+10/1440');

DBMS_JOB.ISUBMIT(3,'BEGIN null; END;',SYSDATE,null);END;/

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 239

Specifying Job Times & Frequencies Probably the most complicated part of using DBMS_JOB is to

get the string expression of the job interval right.– Since it's a string, you must use 2 single quotes to embed strings.– Use date arithmetic to request intervals smaller than a day.

Every hour 'SYSDATE + 1/24'

'NEXT_DAY (TRUNC (SYSDATE), ''SATURDAY'') + 2/24'

Every Sunday at 2 AM

'NEXT_DAY ( ADD_MONTHS (TRUNC (SYSDATE, ''Q''), 3), ''MONDAY'') + 9/24'

First Monday of each quarter, at 9 AM

'TRUNC (LEAST ( NEXT_DAY (SYSDATE, ''MONDAY''), NEXT_DAY (SYSDATE, ''WEDNESDAY''), NEXT_DAY (SYSDATE, ''FRIDAY''))) + 18/24'

Every Monday, Wednesday and Friday at 6 PM

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 240

Other Job Queue Operations Remove a job from the queue.

– Only if the job was submitted from same schema

Export jobs from the queue.– Produces a string that can be used to recreate an existing job in the job queue.– Uses DBMS_JOB.ISUBMIT, retaining current job number.

expjob.sql

BEGIN FOR rec IN (SELECT * FROM USER_JOBS) LOOP DBMS_JOB.REMOVE (rec.job); END LOOP;

Run a job immediately.– Performs an implicit COMMIT in current session.

DBMS_JOB.RUN (my_job#);

Remove all jobs for current schema.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 241

Job Queue Views You can also view the job queue by looking at the

DBA_JOBS_RUNNING, DBA_JOBS and USER_JOBS views.– The following query blends job information with session information

to display currently-executing jobs, who owns them and when they began.

SELECT jr.job job_id ,username username ,jr.this_date start_date ,what job_definition

FROM DBA_JOBS_RUNNING jr ,DBA_JOBS j ,V$SESSION s

WHERE s.sid = jr.sid AND jr.job = j.jobORDER BY jr.this_date;

showjobs.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 242

Setting up the Job Facility Make sure that the correct access is set up for the

DBMS_JOB package.– The default is PUBLIC access. You will have to take special DBA

action if you want to restrict who can run jobs.

You will need to set three parameters in the init.ora (initialization) file for your database instance:– job_queue_processes=N where n is the number of concurrent

background processes permitted. The valid range is 0 through 36.– job_queue_interval=N where N is the interval in seconds to check

the job queue. The valid range is 1 to 3600 (a maximum, therefore, of one hour).

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 243

Error Handling with DBMS_JOB What if your stored procedure fails?

– After 16 attempts, the job facility will mark your job as broken.– Do you want it to try 16 times?– In addition, if your failure raises an unhandled exception, it may

cause the background processes to fail and no longer run any jobs at all.

To avoid unexpected and unhandled failures of scheduled jobs:– Always use the RUN built-in to execute your job in a “test” mode.

Then you can go ahead and submit it.– Always include a WHEN OTHERS exception handler in your job

program which traps any and all problems and automatically sets the job status to broken.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 244

Stopping the Job When It Fails

The WHEN OTHERS exception handler of the calc_totals procedure traps any kind of failure. – Obtains the job number from a packaged function by passing the name

of the procedure.– Uses a call to BROKEN to set the status of the job to “broken”. – Calls log program of package to record that failure took place.– Now the job facility will not try to run this program again. You can go in

and fix the problem.

PROCEDURE calc_totals ISBEGIN ...EXCEPTION WHEN OTHERS THEN job# := job_pkg.job (‘calc_totals’) DBMS_JOB.BROKEN (job#, TRUE); job_pkg.log (‘calc_totals’, ‘FAIL’);END;

spacelog.sqlshowspc.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 245

"Good to Knows" for DBMS_JOB You may find it useful to always wrap your stored procedure call (the

what) inside a BEGIN-END block.– We've noticed some aberrant, difficult to reproduce behavior at times with

"straight" procedure calls.

You will need to fully-qualify all database links (with user name and password) to get them to work properly.

If you find that you submit a job to run immediately and it does not start, perform a COMMIT after your submit.

When a job runs, it picks up the current execution environment for the user.

You can use the DBMS_IJOB to manage the jobs of other users.– DBMS_JOB only allows you to modify characteristics and behavior of jobs

submitted by the current schema.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 246

DBMS_PIPEInter-session

Communication

Built-in Packages: DBMS_PIPE

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 247

DBMS_PIPE Overview Allows communication between different Oracle sessions

through a pipe in the RDBMS Shared Global Area.– Operates outside of database transaction limitations.

Uses for DBMS_PIPE include:– Parallelization of program execution. Oracle uses database pipes

to parallelize database operations. You can parallelize your own code.

– More sophisticated debugging inside PL/SQL programs (work around fundamental limitations of DBMS_OUTPUT).

– Interface PL/SQL-database activities with operating system functions and programs written in other languages.

– Perform and commit DML in a separate transaction space from your main transaction.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 248

Shared Global Area

Architecture of DBMS_PIPE

A pipe is a named object that uses the System Global Area to provide a non-transaction based conduit of information.– The pipe sends/receives a message, which can be composed of one

or more separate packets, using a maximum of 4096 bytes. – Names can be up to 128 chars long (do not use names beginning

with “ORA$”. They are reserved for Oracle use).

“Sue”

Session A

Message Buffer Message Buffer

Session B

“Bob”

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 249

Processing Flow of DBMS_PIPE Construct a message from packets of information.

– Each packet can be a string, date, number, ROWID or RAW.– There is just one message buffer per session.

Send the message to a named pipe.– If there isn’t currently room in the pipe, you can wait for up to

1000 days (or 86400000 seconds) for the pipe to be cleared.– This is the default, so you should always specify a timeout period.

Receive a message from that pipe.– You can wait for up to 1000 days for a message.

Unpack the message packets and take action.– Separate out the individual packets in the message (again: string,

date or number).

pipex1.sqlpipex2.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 250

DBMS_PIPE Module OutlineModule Name Description

CREATE_PIPE Creates a PUBLIC or PRIVATE pipe.

NEXT_ITEM_TYPE Returns the datatype of the next item in the piped message.

PACK_MESSAGE Packs an item into the message buffer for your session.

PURGE Empties the contents of a pipe into your local buffer freeing it for removal, making it a candidate for removal with a LRU algorithm.

RECEIVE_MESSAGE Receives a message from pipe and copies to local buffer.

REMOVE_PIPE Removes a pipe explicitly created via CREATE_PIPE.

RESET_BUFFER Clears your buffer so that PACK_MESSAGE and UNPACK_MESSAGE can work from the first item.

SEND_MESSAGE Sends contents of message buffer to the specified pipe.

UNIQUE_SESSION_NAME Returns name that is unique among all sessions in the database.

UNPACK_MESSAGEUnpacks the next item from the local message buffer and deposits it into the specified local variable.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 251

Creating Public and Private Pipes There are two ways to create pipes: implicitly and

explicitly. – Send messages to non-existent pipe implicitly create it. – Use CREATE_PIPE to create a pipe explicitly.PROCEDURE newpipe IS stat INTEGER;BEGIN stat := DBMS_PIPE.CREATE_PIPE ( pipename => 'bbs', maxpipesize => 20000, private => TRUE);

An explicit pipe can be private (accessible only to sessions with matching userID or SYSDBA privileges).– Specify TRUE for private argument of CREATE_PIPE.

A public pipe is accessible as long as you know its name. – Implicitly-created pipes are always public.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 252

Sending a MessageFUNCTION send_message (pipename IN VARCHAR2, timeout IN INTEGER DEFAULT maxwait, maxpipesize IN INTEGER DEFAULT 8192)RETURN INTEGER;

FOR month_num IN 1 .. 12LOOP DBMS_PIPE.PACK_MESSAGE ( total_sales (month_num));END LOOP;

pipe_stat := DBMS_PIPE.SEND_MESSAGE ( ‘monthly’, 60, 10 * 4096);

IF pipe_stat != 0THEN RAISE could_not_send;

Provide pipe name, seconds you will wait, and new max pipe size

(you can make it bigger, but not smaller).

Fill your message buffer with packets of data.

Send to "monthly" pipe, waiting up to 1 minute,

expanding pipe size to 40 Kbytes.

Status of 0 means message was sent.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 253

Receiving and Unpack a Message

First you pull the message from the pipe and place it in buffer with RECEIVE_MESSAGE.– Specify pipe and number of seconds you will wait before you time out.

– Pipe status of 0 means the message was read successfully.

FUNCTION receive_message (pipename IN VARCHAR2, timeout IN INTEGER DEFAULT maxwait)RETURN INTEGER;

PROCEDURE unpack_message (item OUT VARCHAR2);

PROCEDURE unpack_message (item OUT NUMBER);

PROCEDURE unpack_message (item OUT DATE);

Then you call UNPACK_MESSAGE to extract individual packets from the message.– You need to know the datatype of packet or check it using NEXT_ITEM_TYPE.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 254

A Pipe-based Listener Program Program wakes up every N seconds to read the data from the pipe and analyze

results from assembly line (an intentional "infinite loop"!).

PROCEDURE analyze_assembly_data (every_n_secs IN INTEGER)IS pipe_status INTEGER; prod_total NUMBER;BEGIN LOOP pipe_status := DBMS_PIPE.RECEIVE_MESSAGE ( 'production', every_n_secs);

IF pipe_status = 0 THEN DBMS_PIPE.UNPACK_MESSAGE (prod_total); analyze_production (SYSDATE, prod_total); ELSE RAISE_APPLICATION_ERROR ( -20000, 'Production data unavailable.'); END IF; END LOOP;END;

Wait up to N seconds for the

next report.

If I got something,pass it on to the

computation program.

Stop process if data not received.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 255

Unpacking the Message

Since a message can be composed of packets of different datatypes, you have to make sure that you unpack a packet into the right kind of variable. Either:– You know the datatype and therefore can “hard-code” the correct

variable into the call to UNPACK_MESSAGE.

– Or you use the built-in NEXT_ITEM_TYPE to tell you in advance the datatype of the next packet in the message and take appropriate action.

FUNCTION next_item_type RETURN INTEGER;

Value Description or Data type0 No more items in buffer6 NUMBER9 VARCHAR211 ROWID12 DATE23 RAW

Returns one of the following values:

dbpipe.sqldbpipe.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 256

Parallelizing Your Code with Pipes Oracle uses DBMS_PIPE to improve RDBMS performance;

you can do the same for your application if:– You have multiple CPUs available.– You have processes which can run in parallel.

Suppose I want to calculate my net profit. In order to do so I must first compute total sales, total office expenses and total compensation.– These programs each take 15 minutes, but are not dependent on

each other.

Without pipes, I must execute them sequentially and incur an elapsed time of 45 minutes before I calculate the profits.– The CEO is decidedly unhappy about the delay.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 257

Sequential vs. Parallel Execution

Process A Process B Process C

Start

End

Sequential Execution: Maximum Elapsed Time

Process A Process B Process C

S t a r t

E n d

Parallel Execution: Minimum Elapsed Time

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 258

Parallelizing PL/SQL Execution

The “kick off” programs pass their messages to separate pipes. – Other programs running in the background grab the messages and start their

corresponding calculation program.– When each program is complete, it puts a message (perhaps even a value) into

the pipe.

The “wait” program waits till it receives a message from each program. Then net profits can be computed -- in a much-decreased elapsed time.

BEGIN kick_off_sales_calc; kick_off_exp_calc; kick_off_totcomp_calc;

wait_for_confirmation; calculate_net_profits;END;

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 259

Code to Kick Off and Calculate

PROCEDURE kick_off_sales_calc IS stat INTEGER;BEGIN DBMS_PIPE.PACK_MESSAGE (1995); stat := DBMS_PIPE.SEND_MESSAGE (‘sales’);END;

Send message to start calculations.

PROCEDURE calculate_sales IS stat INTEGER;BEGIN stat := DBMS_PIPE.RECEIVE_MESSAGE (‘sales’); IF stat = 0 THEN lots_of_number_crunching; DBMS_PIPE.PACK_MESSAGE (sales$); stat := DBMS_PIPE.SEND_MESSAGE (‘sales’); ELSE DBMS_PIPE.PACK_MESSAGE (NULL); stat := DBMS_PIPE.SEND_MESSAGE (‘sales’); END IF;END;

Receive the year,calculate sales, and send

back the results.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 260

Waiting for Confirmation

PROCEDURE wait_for_confirmationIS stat INTEGER;BEGIN stat := DBMS_PIPE.RECEIVE_MESSAGE (‘sales’); DBMS_PIPE.UNPACK_MESSAGE (sales$);

stat := DBMS_PIPE.RECEIVE_MESSAGE (‘offexp’); DBMS_PIPE.UNPACK_MESSAGE (offexp$);

stat := DBMS_PIPE.RECEIVE_MESSAGE (‘comp’); DBMS_PIPE.UNPACK_MESSAGE (comp$);END;

Wait for all calculations to finish.

The order in which you wait is insignificant.

PROCEDURE calculate_net_profits ISBEGIN net_profits := sales$ - offexp$ - comp$;END;

Perform final calculation.

parallel.sql

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 261

Other DBMS_PIPE Examples Simple trace package that sends it output either to screen or to pipe.

– Demonstrates the use of toggles and switches in packages.

watch.pkgp_and_l.pkg

Implementation of a system-wide, in-memory cache.– A request for data is passed to a "central" pipe. – A listener program grabs the request (including the return pipe

name), obtains the data, and sends it to the pipe.– The requester reads the information from the pipe.

syscache.pkgsyscache.tst

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 262

DBMS_UTILITY

Built-in Packages: DBMS_UTILITY

GET_TIME

GET_HASH_VALUE

FORMAT_CALL_STACK

. . .

Application

SCHEMAA ‘grab-bag’of miscellaneousoperations

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 263

DBMS_UTILITY Module Outline

ANALYZE_DATABASE Analyze objects in database

ANALYZE_PART_OBJECT Runs ANALYZE TABLE or ANALYZE INDEXfor each partition of the object (Oracle8).

COMMA_TO_TABLE Parses comma-delimited list to index-by table.

COMPILE_SCHEMA Recompile INVALID objects in specified schema.

DATA_BLOCK_ADDRESS_BLOCK Gets block number part of data block address.

DATA_BLOCK_ADDRESS_FILE Gets file number part of data block address.

DB_VERSION Returns database version (Oracle8)

EXEC_DDL_STATEMENT Executes DDL statement (Oracle8).

FORMAT_CALL_STACK Returns execution call stack.

FORMAT_ERROR_STACK Returns error stack.

GET_HASH_VALUE Returns hash value for string.

dbver.pkgdbparm.pkg

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 264

DBMS_UTILITY Module Outline, cont.

GET_PARAMETER_VALUE Retrieves value of database parameter (Oracle8).

GET_TIME Returns "current time" down to hundredth of second.

IS_PARALLEL_SERVER Returns TRUE if in parallel server mode.

MAKE_DATA_BLOCK_ADDRESS Creates data block address from block & file numbers.

NAME_RESOLVE Resolves name of object into component parts.

NAME_TOKENIZE Parses string object designator into components.

PORT_STRING Returns platform and version of database.

TABLE_TO_COMMA Converts list (in index-by-table) of elements into acomma-delimited list (string).

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 265

Sub-Second Timings with GET_TIME GET_TIME returns the number of 100ths of seconds that

have elapsed since an arbitrary point in time.– SYSDATE only reflects times down to the nearest second, so

GET_TIME offers significant additional granularity.– Useful when analyzing individual PL/SQL programs, especially

those that run in sub-second time.

Compare results from consecutive calls to GET_TIME to determine the elapsed time of PL/SQL code execution.

DECLARE v_start BINARY_INTEGER;BEGIN v_start := DBMS_UTILITY.GET_TIME; calc_totals;

DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.GET_TIME - v_start);END;

Basic steps necessary to convert GET_TIME

into a performance analysis tool.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 266

Building a Layer Over GET_TIME

ovrhead.sqlplvtmr.spsplvtmr.spb

BEGIN PLVtmr.capture; calc_totals; PLVtmr.show_elapsed;END;

Clean and lean timing code

PACKAGE PLVtmrIS PROCEDURE turn_on; PROCEDURE turn_off; PROCEDURE set_factor (factor_in IN NUMBER);

PROCEDURE capture ( context_in IN VARCHAR2 := NULL)

PROCEDURE show_elapsed (prefix_in IN VARCHAR2 := NULL, adjust_in IN NUMBER := 0, reset_in IN BOOLEAN := TRUE);END PLVtmr;

Partial package specification

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 267

EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ( DBMS_UTILITY.FORMAT_CALL_STACK);END;

Accessing the Execution Call Stack

"Little known facts" about FORMAT_CALL_STACK:– Contains embedded new-line characters, equivalent to CHR(10).– Most recent program at beginning of "report".– Does not show package elements, only the package name.

----- PL/SQL Call Stack -----object line objecthandle number name

88ce3f74 8 package STEVEN.VALIDATE_REQUEST88e49fc4 2 function STEVEN.COMPANY_TYPE88e49390 1 procedure STEVEN.CALC_NET_WORTH88e2bd20 1 anonymous block

Use in the exception

section to show context of

error.

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 268

Accessing Call Stack Contents The call stack length can easily exceed 255 bytes, which means you cannot pass it

to DBMS_OUTPUT directly. Instead, use a loop to read through the stack.

CREATE OR REPLACE PROCEDURE dispcs IS stk VARCHAR2(10000); next_newline INTEGER; next_line VARCHAR2(255); startpos INTEGER := 1;BEGIN stk := DBMS_UTILITY.FORMAT_CALL_STACK || CHR(10); LOOP next_newline := INSTR (stk, CHR(10), startpos, 1); EXIT WHEN next_newline = 0;

next_line := SUBSTR ( stk, startpos, next_newline - startpos + 1);

DBMS_OUTPUT.PUT_LINE (next_line);

startpos := next_newline + 1; END LOOP;END;

dispcs.spdispcs.tst

callstack.pkgplvcs.sps

04/11/23 Copyright 2001 Steven Feuerstein

PL/SQL Advanced Techniques - page 269

Resolving Names of Stored Code

Code names have many components; the way they are attached also follows a complicated syntax.– Use NAME_RESOLVE to break down an identifier string into its

components easily.

PROCEDURE DBMS_UTILITY.NAME_RESOLVE (name IN VARCHAR2, context IN NUMBER, schema OUT VARCHAR2, part1 OUT VARCHAR2, part2 OUT VARCHAR2, dblink OUT VARCHAR2, part1_type OUT NUMBER, object_number OUT NUMBER);

Possible "Part 1" Types

5 Synonym

7 Procedure

8 Function

9 Package

showcomp.spsnc.pkg

What a chore! All those arguments...but don't see it as a problem, see it as an opportunity...for encapsulation!


Recommended