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 1The 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 2then 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 328
Triggers
Trang 4A 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 5Row-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 6change 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 7When 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 8The 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 9before 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 10portion 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 11For 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 13weekend_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 14delete 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 15As 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 16Attribute 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 17Attribute 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 18Attribute 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 19Attribute 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 20Note 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 21Creating 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 22To 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 2329
Procedures, Functions,
and Packages
Trang 24S 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 25In 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 26The 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 27execute 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 28The 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 29SOMETHING 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 30where 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 31You 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 32Debugging 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 33Creating 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 34select 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 35Oracle 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 36RAISE_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 37You 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 38end 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 39insert 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 40order 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