1. Trang chủ
  2. » Công Nghệ Thông Tin

oracle 9i the complete reference phần 6 doc

108 487 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề An Introduction to PL/SQL
Tác giả Loney, Koch
Trường học Unknown University
Chuyên ngành Database Management
Thể loại Sách tham khảo
Năm xuất bản 2002
Thành phố Unknown City
Định dạng
Số trang 108
Dung lượng 1,77 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

First, we’ll create a table that will store the audit records: drop table BOOKSHELF_AUDIT; create table BOOKSHELF_AUDIT The following row-level BEFORE UPDATE trigger will be executed onl

Trang 1

The Exception Handling section of a PL/SQL block is optional—none of the PL/SQL blocksshown previously in this chapter included an Exception Handling section However, the examples

shown in this chapter have been based on a very small set of known input values with very

limited processing performed

In the following listing, the simple loop for calculating the area of a circle is shown, withtwo modifications (shown in bold) A new variable namedsome_variable is declared in the

Declarations section, and a calculation to determine the variable’s value is created in the Executable

the Radius variable (with an initial value of 3) is processed and a record is inserted into the AREAS

table The second time through the loop, the Radius variable has a value of 4—and the calculation for

some_variable encounters an error:

Trang 2

then insert into AREAS values (0,0);

When the PL/SQL block encounters an error, it scans the Exception Handling section forthe defined exceptions In this case, it finds the ZERO_DIVIDE exception, which is one of the

system-defined exceptions available in PL/SQL In addition to the system-defined exceptions and

user-defined exceptions, you can use the when others clause to address all exceptions not defined

within your Exception Handling section The command within the Exception Handling section for

the matching exception is executed and a row is inserted into the AREAS table The output of the

PL/SQL block is shown in the following listing:

select *

from AREAS;

RADIUS AREA - -

3 28.27

0 0

The output shows that the first Radius value (3) was processed, and the exception was encountered

on the second pass through the loop

NOTE

Once an exception is encountered, you cannot return to your normalflow of command processing within the Executable Commandssection If you need to maintain control within the Executable

Commands section, you should use if conditions to test for possible

exceptions before they are encountered by the program or create anested block with its own local exception handling

The available system-defined exceptions are listed in the “Exceptions” entry in theAlphabetical Reference Examples of user-defined exceptions are shown in Chapters 28 and 29

Trang 3

28

Triggers

Trang 4

A related event occurs Triggers may be used to supplement declarative referentialtrigger defines an action the database should take when some

database-integrity, to enforce complex business rules, or to audit changes to data Thecode within a trigger, called thetrigger body, is made up of PL/SQL blocks (seeChapter 27)

The execution of triggers is transparent to the user Triggers are executed by the databasewhen specific types of data manipulation commands are performed on specific tables Such

commands may include inserts, updates, and deletes Updates of specific columns may also be

used as triggering events As of Oracle8i, triggering events may also include DDL commands and

database events (such as shutdowns and logins)

Because of their flexibility, triggers may supplement referential integrity; they should not beused to replace it When enforcing the business rules in an application, you should first rely on

the declarative referential integrity available with Oracle; use triggers to enforce rules that cannot

be coded through referential integrity

Required System Privileges

To create a trigger on a table, you must be able to alter that table Therefore, you must either own

the table, have the ALTER privilege for the table, or have the ALTER ANY TABLE system privilege

In addition, you must have the CREATE TRIGGER system privilege; to create triggers in another

user’s account (also called aschema), you must have the CREATE ANY TRIGGER system privilege

The CREATE TRIGGER system privilege is part of the RESOURCE role provided with Oracle

Toalter a trigger, you must either own the trigger or have the ALTER ANY TRIGGER systemprivilege You may also alter triggers by altering the tables they are based on, which requires that

you have either the ALTER privilege for that table or the ALTER ANY TABLE system privilege For

information on altering triggers, see “Enabling and Disabling Triggers,” later in this chapter

To create a trigger on a database-level event, you must have the ADMINISTER DATABASETRIGGER system privilege

Required Table Privileges

Triggers may reference tables other than the one that initiated the triggering event For example, if

you use triggers to audit changes to data in the BOOKSHELF table, then you may insert a record

into a different table (say, BOOKSHELF_AUDIT) every time a record is changed in BOOKSHELF

To do this, you need to have privileges to insert into BOOKSHELF_AUDIT (to perform the

A trigger’s type is defined by the type of triggering transaction and by the level at which the

trigger is executed In the following sections, you will see descriptions of these classifications,

along with relevant restrictions

Trang 5

Row-Level Triggers

Row-level triggers execute once for each row affected by a DML statement For the BOOKSHELF

table auditing example described earlier, each row that is changed in the BOOKSHELF table may

be processed by the trigger Row-level triggers are the most common type of trigger; they are often

used in data auditing applications Row-level triggers are also useful for keeping distributed data

in sync Materialized views, which use internal row-level triggers for this purpose, are described in

Chapter 23

Row-level triggers are created using the for each row clause in the create trigger command.

The syntax for triggers is shown in “Trigger Syntax,” later in this chapter

Statement-Level Triggers

Statement-level triggers execute once for each DML statement For example, if a single INSERT

statement inserted 500 rows into the BOOKSHELF table, a statement-level trigger on that table

would only be executed once Statement-level triggers therefore are not often used for data-related

activities; they are normally used to enforce additional security measures on the types of actions

that may be performed on a table

Statement-level triggers are the default type of trigger created via the create trigger command.

The syntax for triggers is shown in “Trigger Syntax,” later in this chapter

BEFORE and AFTER Triggers

Because triggers are executed by events, they may be set to occur immediately before or after

those events Since the events that execute triggers include database DML statements, triggers

can be executed immediately before or after inserts, updates, and deletes For database-level

events, additional restrictions apply; you cannot trigger an event to occur before a login or startup

takes place

Within the trigger, you can reference the old and new values involved in the DML statement

The access required for the old and new data may determine which type of trigger you need “Old”

refers to the data as it existed prior to the DML statement; updates and deletes usually reference

old values “New” values are the data values that the DML statement creates (such as the columns

in an inserted record)

If you need to set a column value in an inserted row via your trigger, then you need to use aBEFORE INSERT trigger to access the “new” values Using an AFTER INSERT trigger would not

allow you to set the inserted value, since the row will already have been inserted into the table

AFTER row-level triggers are frequently used in auditing applications, since they do not fireuntil the row has been modified The row’s successful modification implies that it has passed the

referential integrity constraints defined for that table

INSTEAD OF Triggers

You can use INSTEAD OF triggers to tell Oracle what to doinstead of performing the actions that

invoked the trigger For example, you could use an INSTEAD OF trigger on a view to redirect

inserts into a table or to update multiple tables that are part of a view You can use INSTEAD OF

triggers on either object views (see Chapter 30) or relational views

For example, if a view involves a join of two tables, your ability to use the update command

on records in the view is limited However, if you use an INSTEAD OF trigger, you can tell Oracle

how to update, delete, or insert records in the view’s underlying tables when a user attempts to

Trang 6

change values via the view The code in the INSTEAD OF trigger is executedin place of the

insert, update, or delete command you enter.

In this chapter, you will see how to implement basic triggers INSTEAD OF triggers, whichwere initially introduced to support object views, are described in Chapter 30

Schema Triggers

You can create triggers on schema-level operations such as create table, alter table, drop table,

audit, rename, truncate, and revoke You can even create triggers to prevent users from dropping

their own tables! For the most part, schema-level triggers provide two capabilities: preventing

DDL operations and providing additional security monitoring when DDL operations occur

Database-Level Triggers

You can create triggers to be fired on database events, including errors, logins, logoffs, shutdowns,

and startups You can use this type of trigger to automate database maintenance or auditing actions

Trigger Syntax

The full syntax for the create trigger command is shown in the Alphabetical Reference section of

this book The following listing contains an abbreviated version of the command syntax:

create [or replace] trigger [schema ] trigger

{ before | after | instead of }

{ dml_event_clause

| { ddl_event [or ddl_event]

| database_event [or database_event]

[referencing_clause] [for each row]

Clearly, there is a great deal of flexibility in the design of a trigger The before and after

keywords indicate whether the trigger should be executed before or after the triggering event If

the instead of clause is used, the trigger’s code will be executed instead of the event that caused the

trigger to be invoked The delete, insert, and update keywords (the last of which may include a

column list) indicate the type of data manipulation that will constitute a triggering event When

referring to the old and new values of columns, you can use the defaults (“old” and “new”) or

you can use the referencing clause to specify other names.

Trang 7

When the for each row clause is used, the trigger will be a row-level trigger; otherwise, it will

be a statement-level trigger The when clause is used to further restrict when the trigger is executed.

The restrictions enforced in the when clause may include checks of old and new data values.

For example, suppose we want to track any changes to the Rating value in the BOOKSHELFtable whenever rating values are lowered First, we’ll create a table that will store the audit records:

drop table BOOKSHELF_AUDIT;

create table BOOKSHELF_AUDIT

The following row-level BEFORE UPDATE trigger will be executed only if the Rating value is

lowered This example also illustrates the use of the new keyword, which refers to the new value

of the column, and the old keyword, which refers to the old value of the column.

create or replace trigger BOOKSHELF_BEF_UPD_ROW

before update on BOOKSHELF

for each row

when (new.Rating < old.Rating)

begin

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, Old_Rating, New_Rating, Audit_Date) values

(:old.Title, :old.Publisher, :old.CategoryName, :old.Rating, :new.Rating, Sysdate);

end;

Breaking this create trigger command into its components makes it easier to understand.

First, the trigger is named:

create or replace trigger BOOKSHELF_BEF_UPD_ROW

The name of the trigger contains the name of the table it acts upon and the type of trigger it

is (See “Naming Triggers,” later in this chapter, for information on naming conventions.)

This trigger applies to the BOOKSHELF table; it will be executed before update transactions

have been committed to the database:

before update on BOOKSHELF

Because the for each row clause is used, the trigger will apply to each row changed by the

update statement If this clause is not used, then the trigger will execute at the statement level.

for each row

Trang 8

The when clause adds further criteria to the triggering condition The triggering event not only must be an update of the BOOKSHELF table, but also must reflect a lowering of the Rating value:

when (new.Rating < old.Rating)

The PL/SQL code shown in the following listing is the trigger body The commands shown

here are to be executed for every update of the BOOKSHELF table that passes the when condition.

For this to succeed, the BOOKSHELF_AUDIT table must exist, and the owner of the trigger must

have been granted privileges (directly, not via roles) on that table This example inserts the old

values from the BOOKSHELF record into the BOOKSHELF_AUDIT table before the BOOKSHELF

record is updated

begin

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, Old_Rating, New_Rating, Audit_Date) values

(:old.Title, :old.Publisher, :old.CategoryName, :old.Rating, :new.Rating, Sysdate);

end;

NOTE

When the new and old keywords are referenced in the PL/SQL block,

they are preceded by colons (:)

This example is typical of auditing triggers The auditing activity is completely transparent to

the user who performs the update of the BOOKSHELF table However, the transaction against the

BOOKSHELF table is dependent on the successful execution of the trigger

Combining DML Trigger Types

Triggers for multiple insert, update, and delete commands on a table can be combined into a

single trigger, provided they are all at the same level (row level or statement level) The following

example shows a trigger that is executed whenever an insert or an update occurs Several points

(shown in bold) should stand out in this example:

The update portion of the trigger occurs only when the Rating column’s value is updated.

An if clause is used within the PL/SQL block to determine which of the two commands

invoked the trigger

■ In this example the column to be changed is specified in thedml_event_clause instead

of in the when clause as in prior examples.

drop trigger BOOKSHELF_BEF_UPD_ROW;

create or replace trigger BOOKSHELF_BEF_UPD_INS_ROW

Trang 9

before insert or update of Rating on BOOKSHELF

for each row

begin

if INSERTING then

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, New_Rating, Audit_Date)

values

(:new.Title, :new.Publisher, :new.CategoryName, :new.Rating, Sysdate);

else if not inserting then we are updating the Rating

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, Old_Rating, New_Rating, Audit_Date) values

(:old.Title, :old.Publisher, :old.CategoryName, :old.Rating, :new.Rating, Sysdate);

end if;

end;

Again, look at the trigger’s component parts First, it is named and identified as a before

insert and before update (of Rating) trigger, executing for each row:

create or replace trigger BOOKSHELF_BEF_UPD_INS_ROW

before insert or update of Rating on BOOKSHELF

for each row

The trigger body then follows In the first part of the trigger body, shown in the following

listing, the type of transaction is checked via an if clause Valid transaction types are INSERTING,

DELETING, and UPDATING In this case, the trigger checks to see if the record is being inserted

into the BOOKSHELF table If it is, then the first part of the trigger body is executed The INSERTING

portion of the trigger body inserts the new values of the record into the BOOKSHELF_AUDIT table

begin

if INSERTING then

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, New_Rating, Audit_Date)

values

(:new.Title, :new.Publisher, :new.CategoryName, :new.Rating, Sysdate);

Other transaction types can then be checked In this example, because the trigger executed,

the transaction must be either an insert or an update of the Rating column Since the if clause in

the first half of the trigger body checks for inserts, and the trigger is only executed for inserts and

updates, the only conditions that should execute the second half of the trigger body are updates

of Rating Therefore, no additional if clauses are necessary to determine the DML event type This

Trang 10

portion of the trigger body is the same as in the previous example: prior to being updated, the old

values in the row are written to the BOOKSHELF_AUDIT table

else –- if not inserting then we are updating the Rating

insert into BOOKSHELF_AUDIT

(Title, Publisher, CategoryName, Old_Rating, New_Rating, Audit_Date) values

(:old.Title, :old.Publisher, :old.CategoryName, :old.Rating, :new.Rating, Sysdate);

Combining trigger types in this manner may help you to coordinate trigger development amongmultiple developers, since it consolidates all the database events that depend on a single table

Setting Inserted Values

You may use triggers to set column values during inserts and updates The previous examples in

this chapter set the BOOKSHELF_AUDIT.Audit_Date value to the result of the SYSDATE function

You may also use updates to support different application needs, such as storing an uppercase

version of a value along with the mixed-case version entered by users In that case, you may have

partially denormalized your table to include a column for the derived data Storing this data in an

uppercase format (for this example, in the column UpperPerson) allows you to display data to the

users in its natural format while using the uppercase column during queries

Since the uppercase version of the value is derived data, it may become out of sync with theuser-entered column Unless your application supplies a value for the uppercase version during

inserts, that column’s value will be NULL when a new row is entered.

To avoid this synchronization problem, you can use a database trigger Put a BEFORE INSERTand a BEFORE UPDATE trigger on the table; they will act at the row level As shown in the

following listing, this approach can set a new value for UpperName every time the Name

column’s value is changed in BOOKSHELF_CHECKOUT:

alter table BOOKSHELF_CHECKOUT add (UpperName VARCHAR2(25));

create or replace trigger BOOKSHELF_CHECKOUT_BUI_ROW

before insert or update of Name on BOOKSHELF_CHECKOUT

for each row

begin

:new.UpperName := UPPER(:new.Name);

end;

In this example, the trigger body determines the value for UpperName by using the UPPER

function on the Name column This trigger will be executed every time a row is inserted into

BOOKSHELF_CHECKOUT and every time the Name column is updated The Name and

UpperName columns will thus be kept in sync

Maintaining Duplicated Data

The method of setting values via triggers, shown in the previous section, can be combined with

the remote data access methods described in Chapter 22 As with materialized views, you may

replicate all or some of the rows in a table

Trang 11

For example, you may want to create and maintain a second copy of your application’s auditlog By doing so, you safeguard against a single application erasing the audit log records created

by multiple applications Duplicate audit logs are frequently used in security monitoring

Consider the BOOKSHELF_AUDIT table used in the examples in this chapter A second table,BOOKSHELF_AUDIT_DUP, could be created, possibly in a remote database For this example,

assume that a database link called AUDIT_LINK can be used to connect the user to the database

in which BOOKSHELF_AUDIT_DUP resides (refer to Chapter 22 for details on database links)

drop table BOOKSHELF_AUDIT_DUP;

create table BOOKSHELF_AUDIT_DUP

create or replace trigger BOOKSHELF_AUDIT_AFT_INS_ROW

after insert on BOOKSHELF_AUDIT

for each row

begin

insert into BOOKSHELF_AUDIT_DUP@AUDIT_LINK

(Title, Publisher, CategoryName, New_Rating, Audit_Date)

values (:new.Title, :new.Publisher, :new.CategoryName, :new.New_Rating, :new.Audit_Date);

end;

As the trigger header shows, this trigger executes for each row that is inserted into theBOOKSHELF_AUDIT table It inserts a single record into the BOOKSHELF_AUDIT_DUP table in

the database defined by the AUDIT_LINK database link AUDIT_LINK may point to a database

located on a remote server For asynchronous replication requirements, consider using materialized

views instead (see Chapter 23)

You can see the actions of these triggers by inserting a row into BOOKSHELF:

insert into BOOKSHELF

(Title, Publisher, CategoryName, Rating) values

('HARRY POTTER AND THE CHAMBER OF SECRETS',

'SCHOLASTIC','CHILDRENFIC','4');

1 row created.

select Title from BOOKSHELF_AUDIT;

Trang 12

-HARRY POTTER AND THE CHAMBER OF SECRETS

select Title from BOOKSHELF_AUDIT_DUP;

TITLE

-HARRY POTTER AND THE CHAMBER OF SECRETS

Customizing Error Conditions

Within a single trigger, you may establish different error conditions For each of the error

conditions you define, you may select an error message that appears when the error occurs The

error numbers and messages that are displayed to the user are set via the RAISE_APPLICATION_

ERROR procedure, which may be called from within any trigger

The following example shows a statement-level BEFORE DELETE trigger on the BOOKSHELF

table When a user attempts to delete a record from the BOOKSHELF table, this trigger is executed

and checks two system conditions: that the day of the week is neither Saturday nor Sunday, and

that the Oracle username of the account performing the delete begins with the letters “LIB.” The

trigger’s components will be described following the listing

create or replace trigger BOOKSHELF_BEF_DEL

before delete on BOOKSHELF

WHEN not_library_user THEN

RAISE_APPLICATION_ERROR (-20002, 'Deletions only allowed by Library users');

end;

The header of the trigger defines it as a statement-level BEFORE DELETE trigger:

create or replace trigger BOOKSHELF_BEF_DEL

before delete on BOOKSHELF

There are no when clauses in this trigger, so the trigger body is executed for all deletes.

The next portion of the trigger declares the names of the two exceptions that are definedwithin this trigger:

Trang 13

weekend_error EXCEPTION;

not_library_user EXCEPTION;

The first part of the trigger body contains an if clause that uses the TO_CHAR function on the

SysDate function If the current day is either Saturday or Sunday, then the WEEKEND_ERROR

error condition is raised This error condition, called anexception, must be defined within the

A second if clause checks the User pseudo-column to see if its first three letters are “LIB.” If

the username does not begin with “LIB,” then the NOT_LIBRARY_USER exception is raised In

this example, the operator <> is used; this is equivalent to != (meaning “not equals”)

if SUBSTR(User,1,3) <> 'LIB' THEN

RAISE not_library_user;

end if;

The final portion of the trigger body tells the trigger how to handle the exceptions It begins

with the keyword exception, followed by a when clause for each of the exceptions Each of

the exceptions in this trigger calls the RAISE_APPLICATION_ERROR procedure, which takes two

input parameters: the error number (which must be between –20001 and –20999), and the error

message to be displayed In this example, two different error messages are defined, one for each

of the defined exceptions:

EXCEPTION

WHEN weekend_error THEN

RAISE_APPLICATION_ERROR (-20001, 'Deletions not allowed on weekends');

WHEN not_library_user THEN

RAISE_APPLICATION_ERROR (-20002, 'Deletions only allowed by Library users');

The use of the RAISE_APPLICATION_ERROR procedure gives you great flexibility in managingthe error conditions that may be encountered within your trigger For a further description of

procedures, see Chapter 29

NOTE

The exceptions will still be raised even if the delete operations

find no rows to delete

When a non-“LIB” user attempts to delete a row from BOOKSHELF, this is the result:

delete from BOOKSHELF

where Title = 'MY LEDGER';

Trang 14

delete from BOOKSHELF

* ERROR at line 1:

ORA-20002: Deletions only allowed by Library users

ORA-06512: at "PRACTICE.BOOKSHELF_BEF_DEL", line 17

ORA-04088: error during execution of trigger 'PRACTICE.BOOKSHELF_BEF_DEL'

Calling Procedures Within Triggers

Rather than creating a large block of code within a trigger body, you can save the code as a

stored procedure and call the procedure from within the trigger, by using the call command For

example, if you create an INSERT_BOOKSHELF_AUDIT_DUP procedure that inserts rows into

BOOKSHELF_AUDIT_DUP, you can call it from a trigger on the BOOKSHELF_AUDIT table, as

shown in the following listing:

create or replace trigger BOOKSHELF_AFT_INS_ROW

after insert on BOOKSHELF_AUDIT

for each row

begin

call INSERT_BOOKSHELF_AUDIT_DUP(:new.Title, :new.Publisher,

:new.CategoryName, :new.Old_Rating, :new.New_Rating, :new.Audit_Date);

end;

Naming Triggers

The name of a trigger should clearly indicate the table it applies to, the DML commands that trigger

it, its before/after status, and whether it is a row-level or statement-level trigger Since a trigger name

cannot exceed 30 characters in length, you need to use a standard set of abbreviations when

naming In general, the trigger name should include as much of the table name as possible Thus,

when creating a BEFORE UPDATE, row-level trigger on a table named BOOKSHELF_CHECKOUT,

you should not name the trigger BEFORE_UPDATE_ROW_LEVEL_BC A better name would be

BOOKSHELF_CHECKOUT_BEF_UPD_ROW

Creating DDL Event Triggers

You can create triggers that are executed when a DDL event occurs If you are planning to use

this feature solely for security purposes, you should investigate using the audit command instead.

For example, you can use a DDL event trigger to execute the trigger code for create, alter, and

drop commands performed on a cluster, function, index, package, procedure, role, sequence,

synonym, table, tablespace, trigger, type, user, or view If you use the on schema clause, the

trigger will execute for any new data dictionary objects created in your schema The following

example will execute a procedure named INSERT_AUDIT_RECORD whenever objects are

created within your schema:

create or replace trigger CREATE_DB_OBJECT_AUDIT

after create on schema

begin

call INSERT_AUDIT_RECORD (ora_dict_obj_name);

end;

Trang 15

As shown in this example, you can reference system attributes such as the object name Theavailable attributes are listed in Table 28-1.

To protect the objects within a schema, you may want to create a trigger that is executed for

each attempted drop table command That trigger will have to be a BEFORE DROP trigger:

create or replace trigger PREVENT_DROP

before drop on Practice.schema

begin

if ora_dict_obj_owner = 'PRACTICE'

and ora_dict_obj_name like 'BOO%' and ora_dict_obj_type = 'TABLE' then

RAISE_APPLICATION_ERROR ( -20002, 'Operation not permitted.');

is TCP/IP

if (ora_sysevent =

‘LOGON’)then addr :=

or altered

IF (ora_dict_obj_type =

‘USER’)THEN INSERT INTOevent_table

INSERT INTO event_table(‘Changed object is ‘ ||

ora_dict_obj_name’);

TABLE 28-1. System Event Attributes

Trang 16

Attribute Type Description Example

if (ora_sysevent =

‘ASSOCIATESTATISTICS’)then number_modified :=

ora_dict_obj_name_list(name_list);

INSERT INTO event_table(‘object owner is’ ||

if (ora_sysevent =

‘ASSOCIATESTATISTICS’)then

number_of_modified_

objects :=

ora_dict_obj_owner_list(owner_list);

INSERT INTO event_table(‘This

returns the number

of grantees in thereturn value

if (ora_sysevent =

‘GRANT’) thennumber_of_users :=

ora_grantee(user_list);

end if;

ora_instance_

num

NUMBER Instance number IF (ora_instance_num = 1)

THEN INSERT INTOevent_table(‘1’);

END IF;

TABLE 28-1. System Event Attributes (continued)

Trang 17

Attribute Type Description Example

if (ora_sysevent =

‘ALTER’ andora_dict_obj_type =

‘TABLE’)then alter_column :=

ora_is_alter_column(‘FOO’);

if (ora_sysevent =

‘CREATE’ andora_dict_obj_type =

‘TABLE’ andora_is_creating_

nested_table)then insert intoevent_tab

values (‘A nested table iscreated’);

if (ora_sysevent =

‘ALTER’ andora_dict_obj_type =

‘TABLE’)then drop_column :=

ora_is_drop_column(‘FOO’);

IF(ora_is_servererror(error_number

))THEN INSERT INTOevent_table

Trang 18

Attribute Type Description Example

Retrieve ora_sql_txtinto

sql_text variablefirst

returns the number

of privileges in thereturn value

if (ora_sysevent = ‘GRANT’

orora_sysevent = ‘REVOKE’)then

returns the number

of revokees in thereturn value

if (ora_sysevent =

‘REVOKE’)thennumber_of_users :=

ora_revokee(user_list);

ora_server_error NUMBER Given a position

(1 for top of stack),

it returns the errornumber at thatposition on errorstack

INSERT INTO event_table(‘top

n := ora_server_

error_depth;

This value is used with other functions such as ora_server_error

TABLE 28-1. System Event Attributes (continued)

Trang 19

Attribute Type Description Example

ora_server_

error_msg

(position in

BINARY_INTEGER)

VARCHAR2 Given a position (1

for top of stack), itreturns the errormessage at thatposition on errorstack

INSERT INTO event_table(‘top

stack error message’ ||

BINARY_INTEGER Given a position (1

for top of stack), itreturns the number

of strings that havebeen substitutedinto the errormessage using aformat like "%s"

VARCHAR2 Given a position (1

for top of stack) and

a parameternumber, returns thematching "%s",

"%d", and so onsubstitution value inthe error message

E.g the 2nd %s in amessage

like “Expected %s,found %s”

BINARY_INTEGER Returns the SQL

text of the triggeringstatement in theOUT parameter

If the statement islong, it is broken

up into multiplePL/SQL tableelements Thefunction returnvalue specifies howmany elements are

in the PL/SQL table

sql_textora_name_list_t;

ora_sysevent VARCHAR2(20) System event firing

the trigger: Eventname is same asthat in the syntax

INSERT INTO event_table(ora_sysevent);

TABLE 28-1. System Event Attributes (continued)

Trang 20

Note that the trigger references the event attributes within its if clauses Attempting to drop a

table in the Practice schema whose name starts with BOO results in the following:

drop table BOOKSHELF_AUDIT_DUP;

drop table BOOKSHELF_AUDIT_DUP

*

ERROR at line 1:

ORA-00604: error occurred at recursive SQL level 1

ORA-20002: Operation not permitted.

the privileges aregranted with grantoption

if (ora_sysevent =

‘GRANT’ andora_with_grant_option =TRUE)

then insert intoevent_table

(‘with grant option’);

the error is related

to an out-of-spacecondition, and fills

in the OUTparameters withinformation aboutthe object thatcaused the error

if (space_error_

info(eno, typ,owner, ts, obj, subobj) =TRUE)

thendbms_output.put_line(‘Theobject ‘ || obj || ‘owned by ‘

|| owner || ‘ has run outof

space.’);

end if;

TABLE 28-1. System Event Attributes (continued)

Trang 21

Creating Database Event Triggers

Like DML events, database events can execute triggers When a database event occurs (a

shutdown, startup, or error), you can execute a trigger that references the attributes of the event

(as with DDL events) You could use a database event trigger to perform system maintenance

functions immediately after each database startup

For example, the following trigger pins packages on each database startup Pinning packages

is an effective way of keeping large PL/SQL objects in the shared pool of memory, improving

performance and enhancing database stability This trigger, PIN_ON_STARTUP, will run each

time the database is started

create or replace trigger PIN_ON_STARTUP

after startup on database

begin

DBMS_SHARED_POOL.KEEP ( 'SYS.STANDARD', 'P');

for the full create trigger command syntax.

Enabling and Disabling Triggers

Unlike declarative integrity constraints (such as NOT NULL and PRIMARY KEY), triggers do not

necessarily affect all rows in a table They only affect operations of the specified type, and then

only while the trigger is enabled Any data created prior to a trigger’s creation will not be affected

by the trigger

By default, a trigger is enabled when it is created However, there are situations in which youmay want to disable a trigger The two most common reasons involve data loads During large

data loads, you may want to disable triggers that would execute during the load Disabling the

triggers during data loads may dramatically improve the performance of the load After the data

has been loaded, you need to manually perform the data manipulation that the trigger would

have performed had it been enabled during the data load

The second data load–related reason for disabling a trigger occurs when a data load failsand has to be performed a second time In such a case, it is likely that the data load partially

succeeded—and thus the trigger was executed for a portion of the records loaded During a

subsequent load, the same records would be inserted Thus, it is possible that the same trigger

will be executed twice for the same insert operation (when that insert operation occurs during

both loads) Depending on the nature of the operations and the triggers, this may not be desirable

If the trigger was enabled during the failed load, then it may need to be disabled prior to the start

of a second data load process After the data has been loaded, you need to manually perform the

data manipulation that the trigger would have performed had it been enabled during the data load

Trang 22

To enable a trigger, use the alter trigger command with the enable keyword To use this

command, you must either own the table or have the ALTER ANY TRIGGER system privilege

A sample alter trigger command is shown in the following listing:

alter trigger BOOKSHELF_BEF_UPD_INS_ROW enable;

A second method of enabling triggers uses the alter table command, with the enable all triggers clause You may not enable specific triggers with this command; you must use the alter trigger

command to do that The following example shows the usage of the alter table command:

alter table BOOKSHELF enable all triggers;

To use the alter table command, you must either own the table or have the ALTER ANY

TABLE system privilege

You can disable triggers using the same basic commands (requiring the same privileges) with

modifications to their clauses For the alter trigger command, use the disable clause:

alter trigger BOOKSHELF_BEF_UPD_INS_ROW disable;

For the alter table command, use the disable all triggers clause:

alter table BOOKSHELF disable all triggers;

You can manually compile triggers Use the alter trigger compile command to manually

compile existing triggers that have become invalid Since triggers have dependencies, they can

become invalid if an object the trigger depends on changes The alter trigger debug command

allows PL/SQL information to be generated during trigger recompilation

Replacing Triggers

The status of a trigger is the only portion that can be altered To alter a trigger’s body, the trigger

must be re-created or replaced When replacing a trigger, use the create or replace trigger command

(refer to “Trigger Syntax” and the examples earlier in the chapter)

Dropping Triggers

Triggers may be dropped via the drop trigger command To drop a trigger, you must either own

the trigger or have the DROP ANY TRIGGER system privilege An example of this command is

shown in the following listing:

drop trigger BOOKSHELF_BEF_UPD_INS_ROW;

Trang 23

29

Procedures, Functions,

and Packages

Trang 24

S ophisticated business rules and application logic can be stored asOracle Stored procedures—groups of SQL, PL/SQL, and Java statements—enableprocedures within

you to move code that enforces business rules from your application to the database

As a result, the code will be stored once for use by multiple applications BecauseOracle supports stored procedures, the code within your applications should becomemore consistent and easier to maintain

NOTE

For details on Java and its use in stored procedures, see Chapters 34,

35, and 36 This chapter will focus on PL/SQL procedures

You may group procedures and other PL/SQL commands intopackages In the followingsections, you will see implementation details and recommendations for packages, procedures,

andfunctions (procedures that can return values to the user)

You may experience performance gains when using procedures, for two reasons:

■ The processing of complex business rules may be performed within the database—andtherefore by the server In client-server or three-tier applications, shifting complex processingfrom the application (on the client) to the database (on the server) may dramaticallyimprove performance

■ Because the procedural code is stored within the database and is fairly static, you mayalso benefit from the reuse of the same queries within the database The Shared SQLArea in the System Global Area (SGA) will store the parsed versions of the executedcommands Thus, the second time a procedure is executed, it may be able to takeadvantage of the parsing that was previously performed, improving the performance

of the procedure’s execution

In addition to these advantages, your application development efforts may also benefit

Business rules that are consolidated within the database no longer need to be written into each

application, thus saving you time during application creation and simplifying the maintenance

process

Required System Privileges

To create a procedural object, you must have the CREATE PROCEDURE system privilege (which

is part of the RESOURCE role) If the procedural object will be in another user’s schema, then you

must have the CREATE ANY PROCEDURE system privilege

Executing Procedures

Once a procedural object has been created, it may be executed When a procedural object is

executed, it may rely on the table privileges of its owner, or may rely on the privileges of the user

who is executing it When the procedure is created using the definer’s rights, a user executing the

procedure does not need to be granted access to the tables that the procedure accesses

Trang 25

In prior versions of Oracle, the only option was for the procedural objects to be executedunder the privileges of the procedure owner (calleddefiner rights) As an alternative, you can use

invoker rights, in which case the procedures execute under the privileges of the user executing

the procedure If a procedure uses invoker rights, the user must have access to all of the database

objects accessed by the procedure Unless stated otherwise, the examples in this chapter assume

users are executing procedures under the owner’s privileges

To allow other users to execute your procedural object, grant them the EXECUTE privilege on

that object, as shown in the following example:

grant execute on MY_PROCEDURE to Dora;

The user Dora will now be able to execute the procedure named MY_PROCEDURE If you do not

grant the EXECUTE privilege to users, they must have the EXECUTE ANY PROCEDURE system

privilege in order to execute the procedure

When executed, procedures usually have variables passed to them For example, a procedurethat interacts with the BOOKSHELF table may accept a value for the Title column as its input In

this example, we will assume that the procedure creates a new record in the BOOKSHELF table

when a new book is received (from the list of books in the BOOK_ORDER table) This procedure

can be called from any application within the database (provided the user calling the procedure

has been granted the EXECUTE privilege for it)

The syntax used to execute a procedure depends on the environment from which the

procedure is called From within SQL*Plus, a procedure can be executed by using the execute

command, followed by the procedure name Any arguments to be passed to the procedure must

be enclosed in parentheses following the procedure name, as shown in the following example

(which uses a procedure called NEW_BOOK):

execute NEW_BOOK('ONCE REMOVED');

The command will execute the NEW_BOOK procedure, passing to it the value ONCE REMOVED

From within another procedure, function, package, or trigger, the procedure can be called

without the execute command If the NEW_BOOK procedure was called from a trigger on the

BOOK_ORDER table, the body of that trigger may include the following command:

NEW_BOOK(:new.Title);

The NEW_BOOK procedure will be executed using the new value of the Title column as its

input (See Chapter 28 for further information on the use of old and new values within triggers.)

To execute a procedure owned by another user, you must either create a synonym for thatprocedure or reference the owner’s name during the execution, as shown in the following listing:

execute Practice.NEW_BOOK('ONCE REMOVED');

The command will execute the NEW_BOOK procedure in the Practice schema

Alternatively, a synonym for the procedure could be created by using the following command:

create synonym NEW_BOOK for Practice.NEW_BOOK;

Trang 26

The owner of that synonym would then no longer need to refer to the procedure’s owner to

execute the procedure You could simply enter the command:

execute NEW_BOOK('ONCE REMOVED');

and the synonym would point to the proper procedure

When you execute remote procedures, the name of a database link must be specified (seeChapter 22 for information on database links) The name of the database link must immediately

follow the procedure’s name and precede the variables, as shown in the following example:

execute NEW_BOOK@REMOTE_CONNECT('ONCE REMOVED');

The command uses the REMOTE_CONNECT database link to access a procedure called

NEW_BOOK in a remote database

To make the location of the procedure transparent to the user, a synonym may be created forthe remote procedure, as shown in the following example:

create synonym NEW_BOOK

for NEW_BOOK@REMOTE_CONNECT;

Once this synonym has been created, the user may refer to the remote procedure by usingthe name of the synonym Oracle assumes that all remote procedure calls involve updates in the

remote database

Required Table Privileges

Procedural objects may reference tables For the objects to execute properly, the owner of the

procedure, package, or function being executed must have privileges on the tables it uses Unless

you are using invoker rights, the user who is executing the procedural object does not need

privileges on its underlying tables

NOTE

The privileges needed for procedures, packages, and functions cannotcome from roles; they must be granted directly to the owner of theprocedure, package, or function

Procedures vs Functions

Functions can return a value to the caller, and functions can be directly referenced in queries

This value is returned through the use of the return keyword within the function Examples of

functions are shown in “create function Syntax,” later in this chapter

Procedures vs Packages

Packages are groups of procedures, functions, variables, and SQL statements grouped together

into a single unit To execute a procedure within a package, you must first list the package name,

and then list the procedure name, as shown in the following example:

Trang 27

execute BOOK_INVENTORY.NEW_BOOK('ONCE REMOVED');

Here, the NEW_BOOK procedure within the BOOK_INVENTORY package was executed

Packages allow multiple procedures to use the same variables and cursors Procedures withinpackages may be either available to the public (as is the NEW_BOOK procedure in the prior example)

or private, in which case they are only accessible via commands from within the package (such

as calls from other procedures) Examples of packages are shown in “create package Syntax,” later

in this chapter

Packages may also include commands that are to be executed each time the package is called,regardless of the procedure or function called within the package Thus, packages not only group

procedures but also give you the ability to execute commands that are not procedure-specific

See “Initializing Packages,” later in this chapter, for an example of code that is executed each

time a package is called

create procedure Syntax

The syntax for the create procedure command is shown in the Alphabetical Reference The syntax is

create [or replace] procedure [schema ] procedure

[( argument [ in | out | in out ] [nocopy] datatype

[, argument [ in | out | in out ] [nocopy] datatype]

Both the header and the body of the procedure are created by this command

The NEW_BOOK procedure is created by the command shown in the following listing:

create or replace procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) as

Here, the NEW_BOOK procedure will accept a book’s title, publisher, and category as its input

It can be called from any application It inserts a record into the BOOKSHELF table, with a NULL

value for the Rating column, and deletes the matching Title from the BOOK_ORDER table

If a procedure already exists, you may replace it via the create or replace procedure command.

The benefit of using this command (instead of dropping and re-creating the old procedure) is that

the EXECUTE privilege grants previously made on the procedure will remain in place

Trang 28

The IN qualifier is used for arguments for which values must be specified when calling theprocedure In the NEW_BOOK example, the aTitle, aPublisher, and aCategoryName arguments

are declared as IN The OUT qualifier signifies that the procedure passes a value back to the caller

through this argument The IN OUT qualifier signifies that the argument is both an IN and an

OUT: A value must be specified for this argument when the procedure is called, and the procedure

will return a value to the caller via this argument If no qualifier type is specified, then the default

value is IN

In the create procedure syntax, authid refers to the type of authentication: definer rights (the default) or invoker rights (the current_user) The nocopy keyword tells Oracle to pass the variable

value back to the user as quickly as possible

By default, a procedure consists of a block of code written in PL/SQL (block refers to the codethat the procedure will execute when called) In the NEW_BOOK example, the block is as follows:

The PL/SQL block shown is fairly simple, consisting of a single SQL statement PL/SQL blocks

within procedures can include any DML statement; they cannot be used for DDL statements

(such as create view).

Alternatively, language refers to the language in which the code is written For Java examples,

execute NEW_BOOK('ONCE REMOVED','SANCTUARY PUB','ADULTNF');

PL/SQL procedure successfully completed.

select Title from BOOK_ORDER;

TITLE

-SHOELESS JOE

Trang 29

SOMETHING SO STRONG

GALILEO'S DAUGHTER

LONGITUDE

select * from BOOKSHELF

where Title = 'ONCE REMOVED';

SANCTUARY PUB ADULTNF

create function Syntax

The syntax for the create function command is more complicated than the syntax for the create

procedure command At a high level, the syntax is

create [or replace] function [schema ] function

[( argument [ in | out | in out ] [nocopy] datatype

[, argument [ in | out | in out ] [nocopy] datatype]

)]

return datatype

[{ invoker_rights_clause | deterministic | parallel_enable_clause }

[ invoker_rights_clause | deterministic | parallel_enable_clause ]

]

{ { aggregate | pipelined } using [schema ] implementation_type

| [pipelined] { is | as } { pl/sql_function_body | call_spec }};

Both the header and the body of the function are created by this command

The return keyword specifies the datatype of the function’s return value This can be any valid PL/SQL datatype (see Chapter 27) Every function must have a return clause, since the

function must, by definition, return a value to the calling environment

The following example shows a function named OVERDUE_CHARGES, which returns theoverdue book charges by person, based on calculations against the BOOKSHELF_CHECKOUT

table The input is the person’s name, while the output is the balance for that person

create or replace function OVERDUE_CHARGES (aName IN VARCHAR2)

from BOOKSHELF_CHECKOUT

Trang 30

where Name = aName;

RETURN(owed_amount);

end;

To show the differences between procedures and functions, we’ll look at this function piece

by piece First, the function is named and the input is specified:

create or replace function OVERDUE_CHARGES (aName IN VARCHAR2)

Next, we define the characteristics of the value to be returned The definition of the variablewhose value will be returned has three parts: its datatype, its name, and its length The datatype

in this example is set via the return NUMBER clause The variable is then namedowed_amount,

and is defined as a NUMBER(10,2) Thus, all three parts of the variable—its datatype, its name,

and its length—have been defined

previously defined) The RETURN(owed_amount) command line then returns the value in the

owed_amount variable to the calling program

begin

select SUM(((ReturnedDate-CheckoutDate) -14)*0.20) into owed_amount

from BOOKSHELF_CHECKOUT where Name = aName;

will be created in your schema To create a function in your schema, you must have been granted

the CREATE PROCEDURE system privilege (which is part of the RESOURCE role) Having the

privilege to create procedures gives you the privilege to create functions and packages as well

You can check the function by creating a variable and setting its value equal to the function’sreturned value:

variable owed number;

execute :owed := OVERDUE_CHARGES('FRED FULLER');

PL/SQL procedure successfully completed.

Trang 31

You can verify the results by printing the variable’s value:

print owed

OWED -

3.8

Referencing Remote Tables in Procedures

Remote tables can be accessed by the SQL statements in procedures A remote table can be

queried via a database link in the procedure, as shown in the following example In this example,

the NEW_BOOK procedure inserts a record into the BOOKSHELF table in the database defined

by the REMOTE_CONNECT database link while deleting from a local copy of BOOK_ORDER

create or replace procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) as

begin

insert into BOOKSHELF@REMOTE_CONNECT (Title, Publisher, CategoryName, Rating) values (aTitle, aPublisher, aCategoryName, NULL);

delete from BOOK_ORDER where Title = aTitle;

end;

Procedures may also use local synonyms For example, you may create a local synonym for aremote table, as follows:

create synonym BOOKSHELF for BOOKSHELF@REMOTE_CONNECT;

You can then rewrite your procedure to remove the database link specifications:

create or replace procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) as

begin

insert into BOOKSHELF (Title, Publisher, CategoryName, Rating) values (aTitle, aPublisher, aCategoryName, NULL);

delete from BOOK_ORDER where Title = aTitle;

Trang 32

Debugging Procedures

The SQLPLUS show errors command displays all the errors associated with the most recently

created procedural object This command checks the USER_ERRORS data dictionary view for the

errors associated with the most recent compilation attempt for that object show errors displays

the line and column number for each error, as well as the text of the error message

To view errors associated with previously created procedural objects, you may queryUSER_ERRORS directly, as shown in the following listing This example queries USER_ERRORS

for error messages encountered during the creation of the OVERDUE_CHARGES function shown

earlier in this chapter

select Line, /*Line number of the error */

Position, /*Column number of the error.*/

Text /*Text of the error message.*/

from USER_ERRORS

where Name = 'OVERDUE_CHARGES'

and Type = 'FUNCTION' order by Sequence;

Valid values for the Type column are VIEW, PROCEDURE, PACKAGE, FUNCTION, and

PACKAGE BODY

Two other data dictionary view levels—ALL and DBA—may also be used to retrieve informationabout errors involving procedural objects For information on these views, see Chapter 37

Using the DBMS_OUTPUT Package

In addition to the debugging information provided by the show errors command, you may use

the DBMS_OUTPUT package, one of a set of packages that is automatically installed when you

create an Oracle database

To use DBMS_OUTPUT, you must issue the set serveroutput on command before executing

the procedural object you will be debugging

DBMS_OUTPUT allows you to use three debugging functions within your package:

PUT Puts multiple outputs on the same linePUT_LINE Puts each output on a separate lineNEW_LINE Used with PUT; signals the end of the current output linePUT and PUT_LINE are used to generate the debugging information you want to display Forexample, if you are debugging a procedure that includes a loop (refer to Chapter 27), you may

want to track the changes in a variable with each pass through the loop To track the variable’s

value, you may use a command similar to the one shown in the following listing In this example,

the value of the Owed_Amount column is printed, prefixed by the literal string ‘Owed:’

DBMS_OUTPUT.PUT_LINE('Owed:'||Owed_Amount);

You may also use PUT and PUT_LINE outside of loops, but such uses may be better

accomplished via the use of the RETURN command in functions (refer to “create function

Syntax,” earlier in this chapter)

Trang 33

Creating Your Own Functions

Rather than just calling custom functions via execute commands, you may use them within SQL

expressions This enables you to extend the functionality of SQL, customizing it to your needs

Such functions can be used in the same manner as Oracle-provided functions, such as SUBSTR

and TO_CHAR Custom functions cannot be used in CHECK or DEFAULT constraints and cannot

manipulate any database values

You can call either stand-alone functions (created via the create function command shown

in the previous sections) or functions declared in package specifications (to be covered in “create

package Syntax” later in this chapter) Procedures are not directly callable from SQL, but may be

called by the functions you create

For example, consider the OVERDUE_CHARGES function shown earlier in this chapter,which calculated the total owed for overdue books It had a single input variable—the person’s

name However, to see the OVERDUE_CHARGES results for all of the book borrowers, you

would normally need to execute this procedure once for each record in the BOOKSHELF_

CHECKOUT table

You can improve the overdue charges calculation process Consider the following query:

create view BOOK_BORROWERS

as select distinct Name from BOOKSHELF_CHECKOUT;

select Name,

OVERDUE_CHARGES(Name) from BOOK_BORROWERS;

Oracle returns the following output:

This single query uses the custom OVERDUE_CHARGES function to calculate the overdue

charges for all book borrowers It’s not quite correct—people are getting credit for books they

returned early You can use the create or replace function command to add a where clause to

the function’s query:

create or replace function OVERDUE_CHARGES (aName IN VARCHAR2)

return NUMBER

is

owed_amount NUMBER(10,2);

begin

Trang 34

select SUM(((ReturnedDate-CheckoutDate) -14)*0.20) into owed_amount

from BOOKSHELF_CHECKOUT where Name = aName and ReturnedDate-CheckoutDate > 14;

The query selected each name from BOOK_BORROWERS; for each name, it executed the

OVERDUE_CHARGES function, which selected data from BOOKSHELF_CHECKOUT

To take advantage of this feature, your functions must follow the same guidelines as Oracle’sfunctions Most notably, they must not update the database, and must contain only IN parameters

NOTE

Although this technique works, it has performance costs The moredata there is in the table, the bigger the performance penalty will bewhen comparing this method to a traditional join

Customizing Error Conditions

You may establish different error conditions within procedural objects (refer to Chapter 28 for

examples of customized error conditions within triggers) For each of the error conditions you

define, you may select an error message that will appear when the error occurs The error numbers

and messages that are displayed to the user are set by you via the RAISE_APPLICATION_ERROR

procedure, which may be called from within any procedural object

You can call RAISE_APPLICATION_ERROR from within procedures, packages, and functions

It requires two inputs: the message number and the message text You get to assign both the message

number and the text that will be displayed to the user This is a very powerful addition to the

standard exceptions that are available in PL/SQL (see “Exception” in the Alphabetical Reference)

The following example shows the OVERDUE_CHARGES function defined earlier in this chapter

Now, however, it has an additional section (shown in bold) Titled EXCEPTION, this section tells

Trang 35

Oracle how to handle nonstandard processing In this example, the NO_DATA_FOUND exception’s

standard message is overridden via the RAISE_APPLICATION_ERROR procedure

create or replace function OVERDUE_CHARGES (aName IN VARCHAR2)

from BOOKSHELF_CHECKOUT where Name = aName and (ReturnedDate-CheckoutDate) > 14;

which immediately precedes the begin command As shown in the following listing, this section

should include entries for each of the custom exceptions you have defined, listed as type EXCEPTION:

In the exception portion of the procedural object’s code, you tell the database how to handle the exceptions It begins with the keyword exception, followed by a WHEN clause for each

exception Each exception may call the RAISE_ APPLICATION_ERROR procedure, which takes

two input parameters: the error number (which must be between -20001 and -20999) and the

error message to be displayed In the preceding example, only one exception was defined

Multiple exceptions can be defined, as shown in the following listing; you may use the when

others clause to handle all nonspecified exceptions:

EXCEPTION

when NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20100, 'No books borrowed.');

when some_custom_error THEN

Trang 36

RAISE_APPLICATION_ERROR (-20101, 'Some custom error message.');

The use of the RAISE_APPLICATION_ERROR procedure gives you great flexibility inmanaging the error conditions that may be encountered within procedural objects

Naming Procedures and Functions

Procedures and functions should be named according to the business function they perform or

business rule they enforce There should be no ambiguity about their purpose

The NEW_BOOK procedure shown earlier should be renamed NEW_BOOK performs abusiness function—inserting records into BOOKSHELF and deleting them from BOOK_ORDER—

so its name should reflect that function A better choice for the name would be RECEIVE_BOOK_

ORDER Since it performs a function, a verb (in this case, “receive”) must describe what it does

The name of the procedure should also include the name of the major table(s) it impacts If the

tables are properly named, then the name of the table should be the direct object upon which

the verb acts (in this case, BOOK_ORDER or BOOKSHELF) For the sake of consistency, we will

continue to refer to the procedure as NEW_BOOK for the remainder of this chapter

create package Syntax

When creating packages, the package specification and the package body are created separately

Thus, there are two commands to use: create package for the package specification, and create

package body for the package body Both of these commands require that you have the CREATE

PROCEDURE system privilege If the package is to be created in a schema other than your own,

then you must have the CREATE ANY PROCEDURE system privilege

Here is the syntax for creating package specifications:

create [or replace] package [user.] package

[authid {definer | current_user} ]

{is | as}

package specification;

Apackage specification consists of the list of functions, procedures, variables, constants,cursors, and exceptions that will be available to users of the package

A sample create package command is shown in the following listing In this example, the

BOOK_MANAGEMENT package is created The OVERDUE_CHARGES function and NEW_BOOK

procedure seen earlier in this chapter are included in the package

create or replace package BOOK_MANAGEMENT

as

function OVERDUE_CHARGES(aName IN VARCHAR2) return NUMBER;

procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2);

end BOOK_MANAGEMENT;

Trang 37

You may append the name of the procedural object to the end clause,

as shown in the preceding example This may make it easier tocoordinate the logic within your code

Apackage body contains the blocks and specifications for all of the public objects listed inthe package specification The package body may include objects that are not listed in the package

specification; such objects are said to beprivate and are not available to users of the package

Private objects may only be called by other objects within the same package body A package

body may also include code that is run every time the package is invoked, regardless of the part

of the package that is executed—see “Initializing Packages,” later in this chapter, for an example

The syntax for creating package bodies iscreate [or replace] package body [user.] package body

{is | as}

package body;

The name of the package body should be the same as the name of the package specification

Continuing the BOOK_MANAGEMENT example, its package body can be created via the

create package body command shown in the following example:

create or replace package body BOOK_MANAGEMENT

from BOOKSHELF_CHECKOUT where Name = aName and (ReturnedDate-CheckoutDate) > 14;

RETURN(owed_amount);

EXCEPTION

when NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20100, 'No books borrowed.');

end OVERDUE_CHARGES;

procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) is

begin

insert into BOOKSHELF (Title, Publisher, CategoryName, Rating) values (aTitle, aPublisher, aCategoryName, NULL);

delete from BOOK_ORDER where Title = aTitle;

Trang 38

end NEW_BOOK;

end BOOK_MANAGEMENT;

The create or replace package body command shown in the preceding example combines the create function command for the OVERDUE_CHARGES function with the create procedure

command for the NEW_BOOK procedure The end clauses all have the names of their associated

objects appended to them (shown in bold in the prior listing) Modifying the end clauses in this

manner helps to clarify the ending points of the object’s source code

Additional functions, procedures, exceptions, variables, cursors, and constants may be definedwithin the package body, but they will not be available to the public unless they have been

declared within the package specification (via the create package command) If a user has been

granted the EXECUTE privilege on a package, then that user can access any of the public objects

that are declared in the package specification

Initializing Packages

Packages may include code that is to be run the first time a user executes a function or procedure

in the package within each session In the following example, the BOOK_MANAGEMENT package

body is modified to include a SQL statement that records the current user’s username and the

timestamp for the first time a package component is executed within the session Two new variables

must also be declared in the package body to record these values

Since the two new variables are declared within the package body, they are not available tothe public Within the package body, they are separated from the procedures and functions The

package initialization code is shown in bold in the following listing:

create or replace package body BOOK_MANAGEMENT

from BOOKSHELF_CHECKOUT where Name = aName and (ReturnedDate-CheckoutDate) > 14;

RETURN(owed_amount);

EXCEPTION

when NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20100, 'No books borrowed.');

end OVERDUE_CHARGES;

procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) is

begin

Trang 39

insert into BOOKSHELF (Title, Publisher, CategoryName, Rating) values (aTitle, aPublisher, aCategoryName, NULL);

delete from BOOK_ORDER where Title = aTitle;

end NEW_BOOK;

begin

select User, SysDate

into User_Name, Entry_Date from DUAL;

listing These two variables can then be used by the functions and procedures within the package,

although this example does not use them in the interest of brevity

To execute a procedure or function that is within a package, specify both the package name and the name of the procedure or function in the execute command, as follows:

execute BOOK_MANAGEMENT.NEW_BOOK('SOMETHING SO STRONG','PANDORAS','ADULTNF');

Viewing Source Code for Procedural Objects

The source code for existing procedures, functions, packages, and package bodies can be

queried from the following data dictionary views:

USER_SOURCE For procedural objects owned by the userALL_SOURCE For procedural objects owned by the user or to which the user has

been granted accessDBA_SOURCE For all procedural objects in the databaseSelect information from the USER_SOURCE view via a query similar to the one shown in thefollowing listing In this example, the Text column is selected, ordered by the Line number The

Name of the object and the object Type are used to define which object’s source code is to be

displayed The following example uses the NEW_BOOK procedure shown earlier in this chapter:

select Text

from USER_SOURCE

where Name = 'NEW_BOOK'

and Type = 'PROCEDURE'

Trang 40

order by Line;

TEXT

-procedure NEW_BOOK (aTitle IN VARCHAR2,

aPublisher IN VARCHAR2, aCategoryName IN VARCHAR2) as

the Line column should be used in the order by clause.

Valid values for the Type column are PROCEDURE, FUNCTION, PACKAGE, PACKAGEBODY, JAVA SOURCE, TYPE, and TYPE BODY

Compiling Procedures,

Functions, and Packages

Oracle compiles procedural objects when they are created However, procedural objects may

become invalid if the database objects they reference change The next time the procedural

objects are executed, they will be recompiled by the database

You can avoid this runtime compiling—and the performance degradation it may cause—byexplicitly recompiling the procedures, functions, and packages To recompile a procedure, use

the alter procedure command, as shown in the following listing The compile clause is the only

valid option for this command

alter procedure NEW_BOOK compile;

To recompile a procedure, you must either own the procedure or have the ALTER ANY

PROCEDURE system privilege

To recompile a function, use the alter function command, with the compile clause:

alter function OVERDUE_CHARGES compile;

To recompile a function, you must either own the function or have the ALTER ANY PROCEDURE

system privilege

When recompiling packages, you may either recompile both the package specification andthe body or just recompile the package body By default, both the package specification and the

package body are recompiled You cannot use the alter function or alter procedure command to

recompile functions and procedures stored within a package

Ngày đăng: 07/08/2014, 14:20

TỪ KHÓA LIÊN QUAN