You are on page 1of 25

NEW ORACLE9I DATABASE TRIGGERS

Joe Trezzo, TUSC

INTRODUCTION
Oracle8i introduced an important expansion of database triggers that has been enhanced with each new version. This important expansion extended the use of database triggers beyond tables to allow more flexibility and functionality to be integrated into your database environment and applications. From Oracle 8.1 to Oracle 9.2, this expansion has continued and the power has increased. Most DBAs and developers are unaware or have not taken advantage of these new database triggers, and therefore, the intent of this paper is to provide insight into these new triggers to ensure that they are understood and used to their fullest by both DBAs and developers. This paper concentrates on identifying the new database triggers and associated components of these new triggers. This paper does not focus on database triggers at the table level. The following topics are covered in this paper: Overview of Database Triggers Creation of Database Triggers Security of Database Triggers Enabling and Disabling Database Triggers Database System Events DDL/Client Events Event Attributes Logging Connection Time (LOGON/LOGOFF) Pinning Objects Upon Startup (STARTUP) Audit Trail of Objects (CREATE/ALTER/DROP) Disable the Ability to Drop Objects (DROP) Disable Logins Dynamically (LOGON) Source Version History (CREATE) Log SQL Information Upon Shutdown (SHUTDOWN)

New Database Triggers

Database Trigger Examples (DBA and Developer)

Data Dictionary Views for Database Triggers Re-Creating OBJECT Creation (DBMS_METADATA Package) All examples were executed under Oracle 9.2.0.1.0.
OF

OVERVIEW

DATABASE TRIGGERS

In todays day and age, Oracle applications are being deployed in a variety of ways with a variety of front-end tools being used to build these applications. The constant continues to be the Oracle database flexibility and functionality that can be realized at the database level. Since
Page 1

the introduction of the internet, many companies applications are up and running 24X7X52 and are available from anywhere by anyone. Application logic controls access to these applications, which ultimately controls access to the data. It is always important to have mechanisms in place to ensure that access is only allowed through the application and that activity in the database is monitored. Prior to Oracle8i, Oracle provided the ability to build logic at the table level through database triggers. These triggers were great for added validation and control at the table level for DML (INSERT, UPDATE, DELETE) activity on tables. In Oracle8i, Oracle expanded this last line of defense from table triggers to other event triggers by introducing eight new database triggers. These eight database triggers introduced the extension of this powerful capability to database startup/shutdown, server-side errors, user logons/logoffs, and object creations/alterations/drops. The implicit execution process employed by previous database triggers is the same for these new triggers. Once the database trigger is created and enabled, it is executed based on database actions. No explicit calls are necessary to execute these database triggers. The catproc.sql script that is executed upon a database creation calls two main SQL scripts that create the infrastructure to allow for database trigger execution. These scripts are highlighted below and are located in the ORACLE_HOME/rdbms/admin directory: DBMSSTDX.SQL extends the standard package by creating the DBMS_STANDARD package that contains a variety of functions, including the event attribute functions. DBMSTRIG.SQL creates functions on top of the DBMS_STANDARD functions to allow for easy reference to the event attributes by granting EXECUTE on these functions and creating a PUBLIC synonym for these functions.

A segment of the DBMSTRIG.SQL file is shown below for the ORA_DICT_OBJ_NAME event attribute.
Rem returns the object name on which the DDL statement is being done create or replace function dictionary_obj_name return varchar2 is begin return dbms_standard.dictionary_obj_name; end; / grant execute on dictionary_obj_name to public / create or replace public synonym ora_dict_obj_name for dictionary_obj_name /

CREATION

OF

DATABASE TRIGGERS

A database trigger is created and dropped with the following commands:


CREATE OR REPLACE TRIGGER trigger_name (BEFORE|AFTER) database_trigger_event ON (DATABASE| schema.SCHEMA); DROP TRIGGER trigger_name;

When a database trigger is created, the trigger is checked for syntax, the dependency tree and privileges are checked, and then the trigger is compiled into pcode and stored in the database. Therefore, triggers are similar to stored packages and procedures in the sense of creation, storage, and execution of pcode. The main differences are that database triggers source code is stored in a different data dictionary table and database triggers execute implicitly based on actions, whereas, packages and procedures are explicitly called.
Page 2

If errors occur during the creation or compilation of a database trigger, then the trigger is still created and enabled. If a database event executes that causes the database trigger to execute, the database event will fail. Therefore, if an error occurs during creation or compilation, the trigger needs to be either dropped, fixed and re-created, or disabled to ensure that processing does not stop. To view errors, the SHOW ERRORS command can be executed or the errors can be retrieved from the USER_ERRORS data dictionary view. It is good practice to use the following guidelines when creating database triggers: Keep it simple do not create complicated database triggers, follow standards, and only create a database trigger when truly needed Keep the length of the database trigger code relatively small, if it starts to get lengthy, create a stored procedure and call the procedure from the trigger; likewise if the segment of code in the database trigger is used in other locations of code, make it modular and create a stored procedure and call the procedure Only create a database trigger when needed to avoid unnecessary overhead, since dependent on the level of the database trigger, overhead will be introduced

The maximum size of a database trigger is 32K. This limitation can be increased by turning the body of the trigger into a stored package or procedure.

SECURITY

OF

DATABASE TRIGGERS

In order to create a database trigger, the schema must have one of 3 Oracle system privileges: CREATE TRIGGER: this privilege allows for a schema to create a database trigger on a table they own. CREATE ANY TRIGGER: this privilege allows a schema to create a database trigger on a table owned by another schema. ADMINISTER DATABASE TRIGGER: this privilege allows a schema to create a database wide database trigger. Once a trigger is created, it is executed implicitly. Internally, Oracle fires the trigger in the existing user transaction. However, triggers execute in the same manner as the default of stored packages and procedures, namely, with the creator of the trigger privilege in the trigger. Therefore, if the USER variable is referenced in a trigger, the creator of the trigger is returned, not the user that caused the trigger to fire. If any stored packages or procedures are called from within a trigger, the schema creating the trigger must have EXECUTE privilege on that object. Triggers are the same as stored packages and procedures and therefore, have dependencies that can cause a trigger to become invalidated. Any time a referenced stored package or procedure is modified, the trigger becomes invalidated. If a trigger ever becomes invalidated, then Oracle will attempt to internally re-compile the trigger the next time it is referenced. As a standard, a trigger that becomes invalidated, should be recompiled manually to ensure that the trigger will compile successfully. To compile a trigger manually, the ALTER TRIGGER command is used. This is shown below:
ALTER TRIGGER logon_trigger COMPILE;

To recompile a trigger, you must either own the trigger or have the ALTER ANY TRIGGER system privilege. If a package or procedure referenced in a trigger is dropped, then the trigger becomes
Page 3

invalid. When the trigger is recompiled either manually or automatically by Oracle, it will fail since it will not be able to successfully reference all of its components. In this case, the trigger is marked with VALID WITH ERRORS and the event will fail when executed.

ENABLING

AND

DISABLING DATABASE TRIGGERS

Disabled database triggers are companions to invalid objects. In some respects, a disabled trigger is far more dangerous than an invalid object because it doesnt fail; it just doesnt execute! This can have severe consequences for applications (and, consequently, for business processes) that depend on business logic stored within procedural code in database triggers. For this reason, you MUST run the following script regularly to ensure there are not any disabled triggers that you are not aware of:
SELECT trigger_name, trigger_type, base_object_type, triggering_event FROM user_triggers WHERE status <> 'ENABLED' AND db_object_type IN ('DATABASE ', 'SCHEMA') ORDER BY trigger_name; TRIGGER_NAME TRIGGER_TYPE BASE_OBJECT_TYPE TRIGGERING_EVEN ------------------------ ---------------- ---------------- --------------DB_STARTUP_TRIGGER AFTER EVENT DATABASE STARTUP

Once the triggers are identified, they can be enabled manually or a dynamic SQL or PL/SQL script can be created to build the SQL statements to ENABLE the triggers. To enable database triggers, the following three commands could be executed.
ALTER TRIGGER db_startup_trigger ENABLE; ALTER TRIGGER before_insert_customer ENABLE; ALTER TABLE s_customer ENABLE ALL TRIGGERS; -- enabling a database trigger -- enabling a table trigger -- enabling all triggers on a table

The preceding commands allow you to enable one trigger at a time or all the triggers on a table. To enable all triggers under a schema, the following script can be used to build an ENABLE script dynamically:
SET HEADING OFF SET FEEDBACK OFF SET PAGESIZE 0 SELECT 'ALTER TRIGGER ' || trigger_name || ' ENABLE;' FROM user_triggers ORDER BY table_name; ALTER TRIGGER DB_STARTUP_TRIGGER ENABLE; ALTER TRIGGER BEFORE_INSERT_CUSTOMER ENABLE; ALTER TRIGGER BEFORE_UPDATE_CUSTOMER ENABLE;

This script can be modified to change the word ENABLE to DISABLE and re-executed to dynamically build a DISABLE script. There may be times that you want to disable triggers for a data load or special processing. In this case, the enable commands shown earlier in this section could be modified as shown below:
ALTER TRIGGER DB_STARTUP_TRIGGER DISABLE; ALTER TRIGGER before_insert_customer DISABLE; ALTER TABLE s_customer DISABLE ALL TRIGGERS;
Page 4

Prior to version 7.3 of Oracle (version 2.3 of PL/SQL), triggers were not stored in compiled format. This means every time the trigger was executed, the trigger was compiled and loaded into memory. This caused additional overhead when using database triggers. Therefore, many people kept certain functions outside of database triggers. This consideration has gone away, since the overhead is now significantly reduced with storing the compiled format. In Oracle9i, there is an undocumented parameter _system_trig_enabled that you can set to FALSE to disable event triggers. In Oracle8i, the parameter is not hidden, and is system_trig_enabled. This parameter can be used during an upgrade, downgrade and/or when patching applications.

NEW DATABASE TRIGGERS


The 20 new triggers are broken into two main categories by Oracle, namely, database system events and DDL/client events. For each event, there are event attributes set internally by Oracle when the event takes place. These event attributes can be referenced in the database trigger logic. For example, the CREATE database trigger can reference the schema name, the type of object created, the name of the object, etc. for the object just created.

DATABASE SYSTEM EVENTS


There are six database system event triggers. The six database system event triggers are outlined below, along with a description and the event attributes that are set for each event. Database Trigger
LOGOFF

BEFORE/AFTE R Execution
BEFORE

Description
Executed when a user logs off, at the start of the logoff process

Attribute Event

LOGON

AFTER

STARTUP

AFTER

ora_sysevent ora_login_user ora_instance_num ora_database_name Executed when a user logs ora_sysevent into the database, after a ora_login_user successful login of the user ora_instance_num ora_database_name ora_client_ip_address Executed when the database is ora_sysevent opened; starts a separate ora_login_user transaction and commits after ora_instance_num this trigger is complete ora_database_name Executed when the instance is shutdown; prior to the shutdown of the instance process; not always executed on abnormal shutdown; starts a separate transaction and commits after this trigger is complete ora_sysevent ora_login_user ora_instance_num ora_database_name

SHUTDOWN

BEFORE

Page 5

SERVERERROR

AFTER

SUSPEND

AFTER

Executes when an Oracle error occurs (can check for a specific error number to only execute for (errno=eno)); does not execute for certain errors (1034, 1403, 1422, 1423, 4030); starts a separate transaction and commits after this trigger is complete Executed whenever a server error causes a transaction to be suspended (example: outof-space error)

ora_sysevent ora_login_user ora_instance_num ora_database_name ora_server_error ora_is_servererror space_error_info

ora_sysevent ora_login_user ora_instance_num ora_database_name ora_server_error ora_is_servererror space_error_info

The startup and shutdown triggers can only be created at the database level. The other four database system events can be created at the database or schema levels. The STARTUP trigger returns a success, even if the trigger fails. The SERVERERROR trigger does not execute when the following Oracle errors are returned: ORA-01403: data not found ORA-01422: exact fetch returns more than requested number of rows ORA-01423: error encountered while checking for extra rows in exact fetch ORA-01034: ORACLE not available ORA-04030: out of process memory

For these triggers, Oracle opens an autonomous transaction scope, fires the trigger, and commits any separate transaction.

DDL/CLIENT EVENTS
There are 14 DDL/client event triggers and these can be created at the database level and will execute for all schemas, or these can be created at the schema level and will execute only for the schema it is created for. When a trigger is created at the schema level, the trigger is created in the schema specified and executes only for that schema. This provides a great deal of flexibility depending on your environment and what you want to monitor or respond to. The 14 DDL/client event triggers are outlined below, along with a description and the event attributes that are set for each event. Database Trigger BEFORE/AFTE R Execution Description Attribute Events

Page 6

ALTER

BEFORE/AFTER

DROP

BEFORE/AFTER

ANALYZE

BEFORE/AFTER

ASSOCIATE STATISTICS

BEFORE/AFTER

AUDIT/NOAUDIT

BEFORE/AFTER

COMMENT

BEFORE/AFTER

CREATE

BEFORE/AFTER

ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_type Executed when object ora_dict_obj_name altered ora_dict_obj_owner ora_des_encrypted_password (for ALTER USER events) ora_is_alter_column, ora_is_drop_column (for ALTER TABLE events) ora_sysevent ora_login_user ora_instance_num Executed when object is ora_database_name dropped ora_dict_obj_type ora_dict_obj_name ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num Executed when the analyze ora_database_name command is executed ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num Executed when the associate ora_database_name statistics command is ora_dict_obj_name executed ora_dict_obj_type ora_dict_obj_owner ora_dict_obj_name_list ora_dict_obj_owner_list ora_sysevent Executed when the audit or ora_login_user noaudit command is executed ora_instance_num ora_database_name ora_sysevent ora_login_user Executed when the comment ora_instance_num command is executed ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num ora_database_name Executed when an object is ora_dict_obj_type created ora_dict_obj_name ora_dict_obj_owner ora_is_creating_nested_table (for CREATE TABLE events)

Page 7

DDL

BEFORE/AFTER

Executed when SQL DDL commands are executed (not executed when and ALTER/CREATE DATABASE, CREATE CONTROLFILE, or DDL issued through the PL/SQL procedure interface)

DISASSOCIATE STATISTICS

BEFORE/AFTER

Executed when the disassociate statistics command is executed

GRANT

BEFORE/AFTER

Executed when the grant command is executed

RENAME

BEFORE/AFTER

Executed when the rename command is executed

REVOKE

BEFORE/AFTER

Executed when the revoke command is executed

TRUNCATE

BEFORE/AFTER

Execute when a table is truncated

ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_dict_obj_name_list ora_dict_obj_owner_list ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_grantee ora_with_grant_option ora_privileges ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_owner ora_dict_obj_type ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner ora_revokee ora_privileges ora_sysevent ora_login_user ora_instance_num ora_database_name ora_dict_obj_name ora_dict_obj_type ora_dict_obj_owner

The new triggers are ideal for DBAs to build mechanisms based on certain events. When the database is started, the objects that need to be pinned can now be moved from the startup SQL script to the STARTUP trigger. When the database is shut down, statistics scripts can be executed to log information into monitoring tables with the SHUTDOWN trigger. Error trapping can be enhanced with the SERVERERROR trigger. Capturing user connect time can be handled
Page 8

through the LOGON and LOGOFF triggers. An object audit trail can be created through the CREATE, ALTER, and DROP triggers.

EVENT ATTRIBUTES
With the introduction of the 20 new database triggers, came the creation of attribute events or variables that are set when a certain database trigger event is executed. The previous section highlighted each of the database triggers, along with the event attributes that are set and can be referenced for each trigger. Below is a list of each of the attribute events, the data type and a short description. Attribute Event
ora_client_ip_address ora_database_name ora_des_encrypted_password ora_dict_obj_name ora_dict_obj_name_list (name_list OUT ora_name_list_t) ora_dict_obj_owner

Data Type
VARCHAR2 VARCHAR2(50) VARCHAR2 VARCHAR(30) BINARY_INTEGER VARCHAR(30)

Description Provides the IP address of the client machine when using TCP/IP Provides the database name Provides the DES encrypted password of the user being created or altered Provides the object name of the object being manipulated Provides a list of object names being manipulated Provides the owner of the object being manipulated Provides the owners of the objects being manipulated Provides the type of object being manipulated Provides the number of grantees Provides the instance number. Provides a return value of TRUE if the specified column is altered Provides a return value of TRUE if the current event is creating a nested table Provides a return value of TRUE if the specified column is dropped Provides a return value of TRUE is the error specified is on the error stack Provides the login schema Provides the position in a CREATE TABLE command where the partition clause can be inserted when using the INSTEAD OF trigger Provides the list of privileges being granted

ora_dict_obj_owner_list(owner BINARY_INTEGER _list OUT ora_name_list_t) ora_dict_obj_type ora_grantee( user_list OUT ora_name_list_t) ora_instance_num ora_is_alter_column( column_name IN VARCHAR2) ora_is_creating_nested_table ora_is_drop_column( column_name IN VARCHAR2) ora_is_servererror ora_login_user ora_partition_pos ora_privilege_list( privilege_list OUT VARCHAR(20) BINARY_INTEGER NUMBER BOOLEAN BOOLEAN BOOLEAN BOOLEAN VARCHAR2(30) BINARY_INTEGER BINARY_INTEGER

Page 9

ora_name_list_t) ora_revokee (user_list OUT ora_name_list_t) ora_server_error BINARY_INTEGER

or revoked Provides a list of the revokees of the revoke command Provides the error on the error stack for the position specified in the stack (1 meaning the top of the stack) Provides the total number of errors on the error stack Provides the error on the error stack for the position specified in the stack (1 meaning the top of the stack) Provides the number of strings that have been substituted into the error message on the error stack for the position specified in the stack (1 meaning the top of the stack) Provides the matching substitution value in the error message for the parameter number specified in conjunction with the position specified on the stack ( 1 meaning the top of the stack) Provides the SQL statement of the statement that caused the trigger to execute (if the statement is lengthy, it will separate it into multiple PL/SQL table elements); the value returned specifies the number of elements Provides the system or client event that caused the trigger to execute Provides a return value of TRUE if the privileges are granted with the grant option Provides a return value of true if the error is related to an out-of-space error and provides the object information of the object with the error

NUMBER

ora_server_error_depth ora_server_error_msg (position in binary_integer)

BINARY_INTEGER

VARCHAR2

ora_server_error_num_params (position in binary_integer)

BINARY_INTEGER

ora_server_error_param (position in binary_integer, param in binary_integer)

VARCHAR2

ora_sql_txt (sql_text out ora_name_list_t)

BINARY_INTEGER

ora_sysevent ora_with_grant_option Space_error_info( error_number OUT NUMBER, error_type OUT VARCHAR2, object_owner OUT VARCHAR2, table_space_name OUT VARCHAR2, object_name OUT VARCHAR2, sub_object_name OUT VARCHAR2)

VARCHAR2(20) BOOLEAN

BOOLEAN

These attribute events allow extreme flexibility and functionality in each of the database triggers and should be used as necessary.

DATABASE TRIGGER EXAMPLES (DBA

AND

DEVELOPER)

The power and flexibility is endless with the 20 new database triggers. This section provides a variety of examples to illustrate this power. A list of the examples is provided below. Logging Connection Time (LOGON/LOGOFF)
Page 10

Pinning Objects Upon Startup (STARTUP) Audit Trail of Objects (CREATE/ALTER/DROP) Disable the Ability to Drop Objects (DROP) Disable Logins Dynamically (LOGON) Source Version History (CREATE) Log SQL Information Upon Shutdown (SHUTDOWN)

LOGGING CONNECTION TIME (LOGON/LOGOFF)


The following example creates a logon statistics table and a LOGON and LOGOFF database trigger to capture the time when a user connects/disconnects to/from the database.
CREATE TABLE (sid user_logged start_time end_time session_logon_statistics NUMBER, VARCHAR2(30), DATE, DATE);

CREATE OR REPLACE TRIGGER logon_log_trigger AFTER LOGON ON DATABASE BEGIN INSERT INTO session_logon_statistics (sid, user_logged, start_time) SELECT DISTINCT sid, ora_login_user, SYSDATE FROM v$mystat; END; / CREATE OR REPLACE TRIGGER logoff_log_trigger BEFORE LOGOFF ON DATABASE BEGIN UPDATE session_logon_statistics SET end_time = SYSDATE WHERE sid = (select distinct sid from v$mystat) AND end_time IS NULL; END; /

The SID is selected from the V$MYSTAT view. SELECT privilege must be granted on the V_$MYSTAT view to the creator of these triggers. The following script retrieves the information from the SESSION_LOGON_STATISTICS table.
COLUMN COLUMN COLUMN SELECT user_logged FORMAT a15 start_time FORMAT a20 end_time FORMAT a20 sid, user_logged, TO_CHAR(start_time, 'MM/DD/YYYY HH24:MI:SS') start_time, TO_CHAR(end_time, 'MM/DD/YYYY HH24:MI:SS') end_time FROM session_logon_statistics order by sid, user_logged, start_time; SID USER_LOGGED START_TIME END_TIME ---------- --------------- -------------------- -------------------12 TRIGGER_TEST 01/22/2003 19:11:53 01/22/2003 19:17:22
Page 11

12 13 13 13 13 14

TRIGGER_TEST PLSQL_USER SYS SYS SYS TRIGGER_TEST

01/22/2003 01/22/2003 01/22/2003 01/22/2003 01/22/2003 01/22/2003

19:17:24 19:12:19 19:18:38 19:19:35 19:19:59 19:12:29

01/22/2003 01/22/2003 01/22/2003 01/22/2003

19:17:46 19:18:13 19:19:34 19:19:53

01/22/2003 19:18:03

PINNING OBJECTS UPON STARTUP (STARTUP)


The following example creates a startup mechanism that pins objects in the shared pool. It provides a dynamic and flexible method to control the pinning by merely inserting and deleting from a database table. Because Oracle uses an LRU algorithm for the caching of objects in the Shared Pool, objects can be flushed out of the Shared Pool. If the objects are large, this will cause degradation in performance and possible errors because objects are loaded into the Shared Pool in contiguous segments. Heavily executed or large and important stored PL/SQL program units should be cached or pinned in the Shared Pool. Stored PL/SQL program units get pinned in the object cache and cursors get pinned in the SQL Area. Oracle provides a procedure in the DBMS_SHARED_POOL package to pin these objects. This is the only method of pinning these objects in the shared pool and this package is not created by default. The dbmspool.sql script must be executed under the SYS schema to create this package. By default, even in Oracle 9.2, no objects, not even the STANDARD package is pinned by default. The following is a pinning mechanism that allows stored PL/SQL program units to be pinned or unpinned by inserting or deleting the name of the program unit from a database table. The following command creates the table to store the names of the stored PL/SQL program units to pin. This should be created under a user with DBA privilege.
CREATE TABLE (owner object type objects_to_pin VARCHAR2(30) NOT NULL, VARCHAR2(128) NOT NULL, VARCHAR2(1) NOT NULL);

The following is a basic method of inserting objects to pin into the table. This can be modified to be more robust and often a Forms front-end interface is created to allow for this function.
INSERT INTO objects_to_pin (owner, object, type) VALUES (UPPER('&owner'), UPPER('&object'), UPPER('&type'));

The type needs to follow the same conventions as defined in the SHARED_POOL package. This is outlined in the creation script of the DBMS_SHARED_POOL script (dbmspool.sql) and is included for reference below:
Value ----P Q R T JS JC Kind of Object to keep ---------------------package/procedure/function sequence trigger type java source java class
Page 12

JR JD C

java resource java shared data cursor

The following procedure takes one parameter to signify if the objects in the table should be pinned or unpinned. The default is to pin the objects. A U as input will unpin the objects in the table.
CREATE OR REPLACE PROCEDURE pin_objects (p_pin_flag_txt IN VARCHAR2 := 'P') IS -- The p_pin_flag_txt is either 'P' for pin -- or 'U' for unpin. CURSOR cur_pin_objects IS SELECT owner || '.' owner, object, type FROM objects_to_pin ORDER BY owner, object; BEGIN FOR cur_pin_objects_rec IN cur_pin_objects LOOP IF p_pin_flag_txt = 'U' THEN DBMS_SHARED_POOL.UNKEEP(cur_pin_objects_rec.owner || cur_pin_objects_rec.object, cur_pin_objects_rec.type); ELSE DBMS_SHARED_POOL.KEEP(cur_pin_objects_rec.owner || cur_pin_objects_rec.object, cur_pin_objects_rec.type); END IF; END LOOP; END pin_objects; /

The pin_objects procedure should be called from the database startup script to make certain the PL/SQL objects are pinned immediately, which will ensure the Shared Pool space is contiguously allocated in memory. In Oracle 8.1, the pin_objects procedure can be moved from the database startup script to the new STARTUP database trigger. The STANDARD package and SOURCE_HISTORY were insert into the OBJECTS_TO_PIN table using the INSERT script above. The following output displays the contents of this table.
COLUMN object FORMAT a30 SELECT * FROM objects_to_pin; OWNER --------------SYS TRIGGER_TEST OBJECT -----------------------------STANDARD SOURCE_HISTORY TYPE -----------P R

The following query displays the stored PL/SQL program units information regarding pinning. The script below has been modified to focus on the two objects that we are attempting to pin. The WHERE clause would usually only contain the following condition.
WHERE kept = 'YES'

The modified script is shown and executed below.


COLUMN owner FORMAT a15 COLUMN name FORMAT a25
Page 13

COLUMN type FORMAT a12 SET PAGESIZE 58 SELECT owner, name, type, kept FROM v$db_object_cache WHERE name in ('STANDARD', 'SOURCE_HISTORY'); OWNER --------------SYS SYS TRIGGER_TEST TRIGGER_TEST NAME ------------------------STANDARD STANDARD SOURCE_HISTORY SOURCE_HISTORY TYPE -----------PACKAGE PACKAGE BODY TABLE TRIGGER KEP --NO NO NO NO

The previous query can be executed to list the sharable memory (sharable_mem column) required for the object and the number of times the object was loaded and executed (loads and executions columns) to determine which objects should be pinned. In order to take this example full circle, a database startup script is created that calls the PIN_OBJECTS procedure. This will ensure that objects will be pinned upon startup no matter where the database is being started from (whether manually or through a script).
CREATE OR REPLACE TRIGGER db_startup_trigger AFTER STARTUP ON DATABASE BEGIN pin_objects; END; /

The database is then shutdown and started up and the query against the V$DB_OBJECT_CACHE view is executed again as shown below.
SQL> shutdown Database closed. Database dismounted. ORACLE instance shut down. SQL> startup ORACLE instance started. Total System Global Area Fixed Size Variable Size Database Buffers Redo Buffers Database mounted. Database opened. OWNER --------------SYS SYS TRIGGER_TEST TRIGGER_TEST 135338868 453492 109051904 25165824 667648 bytes bytes bytes bytes bytes

NAME ------------------------STANDARD STANDARD SOURCE_HISTORY SOURCE_HISTORY

TYPE -----------PACKAGE PACKAGE BODY TABLE TRIGGER

KEP --YES YES NO YES

Pinning frequently executed Oracle objects and application objects is recommended, but remember adequate space must still exist in the Shared Pool for the objects that are not pinned.
Page 14

AUDIT TRAIL

OF

OBJECTS (CREATE/ALTER/DROP)

The next example provides the ability to build your own flexible object audit trail. The example below creates database triggers to capture and log every CREATE, ALTER and DROP operation on the database. First a table is created to store the audit information.
CREATE TABLE audit_object_mods (mod_date DATE, type_of_mod VARCHAR2(20), mod_user VARCHAR2(30), instance_num NUMBER, database_name VARCHAR2(50), object_owner VARCHAR2(30), object_type VARCHAR2(20), object_name VARCHAR2(30));

Next, the CREATE, ALTER, and DROP database triggers are created. These are created for all schemas. If the audit was only focused on one particular schema, then the ON DATABASE line would be changed to ON TRIGGER_TEST.SCHEMA where TRIGGER _TEST is the name of the schema desired to audit.
CREATE OR REPLACE TRIGGER create_object_trigger AFTER CREATE ON DATABASE BEGIN INSERT INTO audit_object_mods (mod_date, type_of_mod, mod_user, instance_num, database_name, object_owner, object_type, object_name) VALUES (sysdate, ora_sysevent, ora_login_user, ora_instance_num, ora_database_name, ora_dict_obj_owner, ora_dict_obj_type, ora_dict_obj_name); END; / CREATE OR REPLACE TRIGGER alter_object_trigger AFTER ALTER ON DATABASE BEGIN INSERT INTO audit_object_mods (mod_date, type_of_mod, mod_user, instance_num, database_name, object_owner, object_type, object_name) VALUES (sysdate, ora_sysevent, ora_login_user, ora_instance_num, ora_database_name, ora_dict_obj_owner, ora_dict_obj_type, ora_dict_obj_name); END; / CREATE OR REPLACE TRIGGER drop_object_trigger AFTER DROP ON DATABASE BEGIN INSERT INTO audit_object_mods (mod_date, type_of_mod, mod_user, instance_num, database_name,
Page 15

object_owner, object_type, object_name) VALUES (sysdate, ora_sysevent, ora_login_user, ora_instance_num, ora_database_name, ora_dict_obj_owner, ora_dict_obj_type, ora_dict_obj_name); END; /

At this point, the audit is set up and as these operations take place in the database, they will be logged to the AUDIT_OBJECT_MODS table. To view the audit, the following script can be executed.
COLUMN type_of_mod FORMAT a10 COLUMN mod_user FORMAT a12 COLUMN database_name FORMAT a10 COLUMN object_owner FORMAT a12 COLUMN object_type FORMAT a10 COLUMN object_name FORMAT a25 SET LINESIZE 130 SELECT mod_date, type_of_mod, mod_user, instance_num, database_name, object_owner, object_type, object_name FROM audit_object_mods ORDER BY mod_date, type_of_mod, object_owner, object_type, object_name;
MOD_DATE --------27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 TYPE_OF_MO ---------CREATE CREATE CREATE CREATE DROP CREATE ALTER ALTER MOD_USER I_NUM DATABASE_N OBJECT_OWNER OBJECT_TYP ------------ ----- ---------- ------------ ---------TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST PROCEDURE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TABLE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER OBJECT_NAME ------------------------ALTER_OBJECT_TRIGGER DROP_OBJECT_TRIGGER PIN_OBJECTS DB_STARTUP_TRIGGER DATABASE_STARTUP_TRIGGER TEST14 DB_STARTUP_TRIGGER DB_STARTUP_TRIGGER

The individual CREATE, ALTER, and DROP database triggers created previously can be replaced with one trigger creation with the use of the OR condition as shown below.
CREATE OR REPLACE TRIGGER c_a_d_object_trigger AFTER CREATE OR ALTER OR DROP ON DATABASE BEGIN INSERT INTO audit_object_mods (mod_date, type_of_mod, mod_user, instance_num, database_name, object_owner, object_type, object_name) VALUES (sysdate, ora_sysevent, ora_login_user, ora_instance_num, ora_database_name, ora_dict_obj_owner, ora_dict_obj_type, ora_dict_obj_name); END; /

DISABLE

THE

ABILITY

TO

DROP OBJECTS (DROP)

The following example illustrates how a trigger can be created for specific schema to disallow the ability to DROP objects. The trigger is for the PLSQL_USER.
Page 16

CREATE OR REPLACE TRIGGER stop_drop_trigger BEFORE DROP ON plsql_user.SCHEMA BEGIN RAISE_APPLICATION_ERROR ( num => -20000, msg => 'Cannot DROP Objects'); END; /

When the PLSQL_USER attempts to DROP any object an error will result as shown below.
SHOW USER USER is "PLSQL_USER" DROP TABLE temp; drop table temp * ERROR at line 1: ORA-00604: error occurred at recursive SQL level 1 ORA-20000: Cannot DROP Objects ORA-06512: at line 2

DISABLE LOGINS DYNAMICALLY (LOGON)


Oracle has added features and commands to allow a DBA to perform more and more of their responsibilities while the database is up and running, thus reducing the time of off-hours administration. With database triggers, the logon trigger allows a DBA to disable new logons by setting a flag in a table. The following table can be used for this toggle flag.
CREATE TABLE logons_allowed (logons_allowed VARCHAR2(3) NOT NULL);

A record is inserted into this table with the value of YES. The following logon trigger is created to reference this table upon logon and look for the value of the record to determine if logins are allowed.
CREATE OR REPLACE TRIGGER logon_allow_trigger AFTER LOGON ON DATABASE DECLARE CURSOR logon_allowed IS SELECT logons_allowed FROM logons_allowed; lv_allowed logons_allowed.logons_allowed%TYPE; BEGIN OPEN logon_allowed; FETCH logon_allowed INTO lv_allowed; IF lv_allowed != 'YES' THEN CLOSE logon_allowed; RAISE_APPLICATION_ERROR ( num => -20000, msg => 'Logins Not Allowed.'); ELSE CLOSE logon_allowed; END IF;
Page 17

END; /

The trigger looks for a value of YES and if the value is NOT YES, then the login will not succeed and the login will fail with an error. To illustrate, the table is updated and the logons_allowed value is set to NO. A user attempts to login and they get the following error:
SQL*Plus: Release 9.2.0.1.0 - Production on Wed Jan 22 21:45:27 2003 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

ERROR: ORA-00604: error occurred at recursive SQL level 1 ORA-20000: Logins Not Allowed. ORA-06512: at line 12

Any time an error is returned either at runtime as forced in the above trigger or if the above trigger was INVALID due to an error upon creation, the SYS and SYSTEM users are exempt from errors in the logon triggers and will be allowed to logon. Likewise, any schema with ADMINISTER DATABASE TRIGGER system privilege follows the same logic. Therefore, when the creator of the trigger above (TRIGGER_TEST who has the above privilege), as well as the SYS and SYSTEM users attempt to logon, they succeed. If the trigger was created with an error and is INVALID, the 3 users and any schema with the privilege above would still succeed upon logon. However, if the trigger was created with an error and is INVALID and anyone else attempts to logon, they will receive the following message.
SQL*Plus: Release 9.2.0.1.0 - Production on Tue Jan 28 15:06:21 2003 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

ERROR: ORA-04098: trigger 'TRIGGER_TEST.LOGON_ALLOW_TRIGGER' is invalid and failed re-validation

Enter user-name:

SOURCE VERSION HISTORY (CREATE)


The ability to view the source code of stored PL/SQL objects in the database provides a lot of advantages, however, the biggest disadvantage is that developers often times bypass version control mechanisms in place to make a quick fix and forget to update the main source code version of the PL/SQL object that resides outside of the database. The problem comes in when the next change is made the correct way by modifying the code outside the database and then recreating it in the database. The prior change made in the database is now lost. I have heard about this many times. The new triggers provide a mechanism to help this problem and create an audit trail/version of all PL/SQL source code created in the database. This is accomplished by inserting the new source code for an object into a history table each time an object is created or recreated. It is accomplished behind the scenes with the CREATE trigger. Each of the creations is also timestamped with the creation date. A history table is first created that mirrors the DBA_SOURCE table as shown below.
Page 18

CREATE TABLE (change_date owner name type line text

source_history DATE NOT VARCHAR2(30) NOT VARCHAR2(30) NOT VARCHAR2(20), NUMBER NOT VARCHAR2(4000));

NULL, NULL, NULL, NULL,

A CREATE trigger is then created that will insert into the SOURCE_HISTORY table after each new object creation as shown below.
CREATE OR REPLACE trigger source_history AFTER CREATE ON DATABASE BEGIN INSERT INTO source_history SELECT SYSDATE, owner, name, type, line, text FROM dba_source WHERE owner = ORA_DICT_OBJ_OWNER AND name = ORA_DICT_OBJ_NAME AND type = ORA_DICT_OBJ_TYPE; END source_history; /

The same scripts created for the DBA_SOURCE view can now be used on this view by only changing the view name to SOURCE_HISTORY. A sample script is shown below with the query limited to one object.
COLUMN owner FORMAT a12 COLUMN name FORMAT a11 COLUMN line FORMAT 9999 COLUMN text FORMAT a60 WORD_WRAPPED SELECT change_date, owner, name, type, line, text FROM source_history WHERE name = 'PIN_OBJECTS' order by change_date, owner, name, type, line;
CHANGE_DA --------27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 27-JAN-03 OWNER -----------TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST TRIGGER_TEST NAME ----------PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS PIN_OBJECTS TYPE LINE TEXT ---------- ----- -----------------------------------------------------------PROCEDURE 1 PROCEDURE pin_objects PROCEDURE 2 (p_pin_flag_txt IN VARCHAR2 := 'P') IS PROCEDURE 3 -- The p_pin_flag_txt is either 'P' for pin PROCEDURE 4 -- or 'U' for unpin. PROCEDURE 5 CURSOR cur_pin_objects IS PROCEDURE 6 SELECT owner || '.' owner, PROCEDURE 7 object PROCEDURE 8 FROM objects_to_pin PROCEDURE 9 ORDER BY owner, object; PROCEDURE 10 BEGIN PROCEDURE 11 FOR cur_pin_objects_rec IN cur_pin_objects LOOP PROCEDURE 12 IF p_pin_flag_txt = 'U' THEN PROCEDURE 13 DBMS_SHARED_POOL.UNKEEP(cur_pin_objects_rec.owner || PROCEDURE 14 cur_pin_objects_rec.object, 'P'); PROCEDURE 15 ELSE PROCEDURE 16 DBMS_SHARED_POOL.KEEP(cur_pin_objects_rec.owner || PROCEDURE 17 cur_pin_objects_rec.object, 'P'); PROCEDURE 18 END IF; PROCEDURE 19 END LOOP; PROCEDURE 20 END pin_objects;

This query can also be changed to include the time .


Page 19

LOG SQL INFORMATION UPON SHUTDOWN (SHUTDOWN)


The following illustrates a method of logging information when the database is shutdown. This can be extended to log more information or perform certain operations. The example logs information from the V$SQLAREA view. First, a log table is created with a timestamp column as shown below.
CREATE TABLE sqlarea_history (log_date DATE, disk_reads NUMBER, executions NUMBER, execution_ratio NUMBER, sql_text VARCHAR2(1000));

A procedure is created to log the desired information from the view into the log table. There is a low threshold in the example below for the number of READS. This can be modified for your system to only log the SQL for statements over a large number of disk reads.
CREATE OR REPLACE PROCEDURE SQLAREA_LOG AS CURSOR c1 IS SELECT disk_reads reads, executions exe, ROUND(disk_reads/DECODE(executions, 0, 1, executions)) ratio, sql_text text FROM v$sqlarea WHERE disk_reads > 100 ORDER BY disk_reads/DECODE(executions, 0, 1, executions) DESC; lv_current_date DATE := SYSDATE; BEGIN FOR c1_rec in c1 LOOP INSERT INTO sqlarea_history (log_date, disk_reads, executions, execution_ratio, sql_text) VALUES (lv_current_date, c1_rec.reads, c1_rec.exe, c1_rec.ratio, c1_rec.text); END LOOP; END; /

Lastly, the database shutdown trigger is created to call the SQLAREA_LOG procedure.
CREATE OR REPLACE TRIGGER db_shutdown_trigger BEFORE SHUTDOWN ON DATABASE BEGIN sqlarea_log; END; /

The SQL can then be queried at anytime in the future with the following statement.
COLUMN sql_text FORMAT a25 COLUMN log_date FORMAT a19 SET PAGESIZE 58 SELECT TO_CHAR(log_date, 'MM/DD/YYYY:HH24:MI:SS') log_date, disk_reads, executions exe, execution_ratio ratio, sql_text FROM sqlarea_history;
Page 20

LOG_DATE DISK_READS EXE RATIO SQL_TEXT ------------------- ---------- ---------- ---------- ------------------------01/22/2003:22:55:09 379 1 379 select o.owner#,o.obj#,de code(o.linkname,null, dec ode(u.name,null,'SYS',u.n ame),o.remoteowner), o.na me,o.linkname,o.namespace ,o.subname from user$ u, obj$ o where u.user#(+)=o .owner# and o.type#=:1 an d not exists (select p_ob j# from dependency$ where p_obj# = o.obj#) for upd ate 01/22/2003:22:55:09 213 1 213 select distinct i.obj# fr om sys.idl_ub1$ i where i .obj#>=:1 and i.obj# not in (select d.p_obj# from sys.dependency$ d) 01/22/2003:22:55:09 115 1 115 select distinct d.p_obj#, d.p_timestamp from sys.de pendency$ d, obj$ o where d.p_obj#>=:1 and d.d_obj #=o.obj# and o.status!=5

DATA DICTIONARY VIEWS

FOR

DATABASE TRIGGERS

There are several data dictionary views that reveal information about triggers. The main two views center on the source code view that contains the source code for package, procedures and functions, but also stores the compilation status of triggers. This is important to determine if a trigger has been compiled successfully. The source code for triggers and the important information regarding being ENABLED or DISABLED is also stored in the trigger views. There are 3 views for each that center on the scope of the information. These are listed below.
user_triggers all_triggers dba_triggers user_source all_source dba_source

Some examples follow to exemplify the usefulness of these views. The first example script provides information on the compilation status of triggers. A status of VALID indicates the trigger is compiled and is ready for execution. A status of INVALID means that the trigger needs to be compiled prior to execution. This will be automatically attempted by Oracle the next time the trigger is fired or manually with the ALTERCOMPILE command prior to the next firing of the trigger. I always recommend recompiling manually to ensure the compilation will be valid and if
Page 21

not, then changes can be made until it does become VALID. You always want to know prior to production if there will be compilation issues that you need to address.
COLUMN SELECT FROM WHERE object_name FORMAT a24 object_name, object_type, status user_objects object_type = 'TRIGGER'; OBJECT_TYP ---------TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER TRIGGER STATUS ------VALID VALID VALID VALID VALID VALID VALID VALID VALID VALID VALID VALID

OBJECT_NAME -----------------------ALTER_OBJECT_TRIGGER CREATE_OBJECT_TRIGGER DB_SHUTDOWN_TRIGGER DB_STARTUP_TRIGGER DROP_OBJECT_TRIGGER LOGOFF_LOG_TRIGGER LOGON_ALLOW_TRIGGER LOGON_LOG_TRIGGER SOURCE_HISTORY STOP_DROP_TRIGGER TEST TEST_LOGON

As evidenced in the output, all triggers are VALID at the current time. If an object that is referenced by one of these triggers is dropped or altered in any manner, this will cause the trigger to become INVALID. The second example provides information about the triggers in the system. This is useful to understand the database and schema triggers that are defined in a database.
COLUMN COLUMN SELECT FROM WHERE trigger_name FORMAT a24 triggering_event FORMAT a15 trigger_name, trigger_type, base_object_type, triggering_event, status user_triggers db_object_type IN ('DATABASE ', 'SCHEMA'); TRIGGER_TYPE ---------------AFTER EVENT AFTER EVENT BEFORE EVENT AFTER EVENT AFTER EVENT BEFORE EVENT AFTER EVENT AFTER EVENT AFTER EVENT AFTER EVENT BEFORE EVENT AFTER EVENT BASE_OBJECT_TYPE ---------------DATABASE DATABASE DATABASE DATABASE DATABASE DATABASE DATABASE DATABASE DATABASE DATABASE SCHEMA SCHEMA TRIGGERING_EVEN --------------ALTER CREATE SHUTDOWN STARTUP DROP LOGOFF LOGON LOGON CREATE LOGON DROP LOGON STATUS -------ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED ENABLED

TRIGGER_NAME -----------------------ALTER_OBJECT_TRIGGER CREATE_OBJECT_TRIGGER DB_SHUTDOWN_TRIGGER DB_STARTUP_TRIGGER DROP_OBJECT_TRIGGER LOGOFF_LOG_TRIGGER LOGON_ALLOW_TRIGGER LOGON_LOG_TRIGGER SOURCE_HISTORY TEST_LOGON STOP_DROP_TRIGGER TEST

The output identifies the triggers with the type of triggers, events that cause each to execute, the timing of the execution (when) and the execution status. Refer to the previous section on ENABLING and DISABLING triggers for more information on this view.
Page 22

The last example provides the source code of each of the triggers (limited to the DB_STARTUP_TRIGGER trigger).
COLUMN COLUMN COLUMN SELECT FROM WHERE trigger_name FORMAT a24 triggering_event FORMAT a15 trigger_body FORMAT a25 word_wrapped trigger_name, trigger_type, base_object_type, triggering_event, trigger_body user_triggers trigger_name = 'DB_STARTUP_TRIGGER';

TRIGGER_NAME TRIGGER_TYPE BASE_OBJECT_TYPE TRIGGERING_EVEN TRIGGER_BODY ------------------ ------------ ---------------- --------------------------------------DB_STARTUP_TRIGGER AFTER EVENT DATABASE STARTUP BEGIN pin_objects; END;

The output provides attributes on the triggers, as well as, the entire contents of the trigger that was created.

RE-CREATING OBJECT CREATION (DBMS_METADATA PACKAGE)


In Oracle9i, there is a new package called DBMS_METADATA that provides an API to the object creation layer. This package provides a variety of procedures and functions (19 total) to allow manipulation of this information. We will concentrate on the GET_DDL function that allows the object creation statements to be retrieved. The following is the DESCRIBE of this function.
DESCRIBE dbms_metadata (output only shows the get_ddl function) FUNCTION GET_DDL RETURNS CLOB Argument Name -----------------------------OBJECT_TYPE NAME SCHEMA VERSION MODEL TRANSFORM Type ----------------------VARCHAR2 VARCHAR2 VARCHAR2 VARCHAR2 VARCHAR2 VARCHAR2 In/Out -----IN IN IN IN IN IN Default? -------DEFAULT DEFAULT DEFAULT DEFAULT

The creation statements of the procedures and triggers created in this paper can be retrieved via the DBMS_METADATA.GET_DDL function in the following code segment. The output is displayed to the screen, but could be directed to a flat file via the UTL_FILE package.
SET LONG 1000 SELECT name, type, DBMS_METADATA.GET_DDL(type, name) FROM user_source WHERE line = 1;
NAME TYPE DBMS_METADATA.GET_DDL(TYPE,NAME) ------------------------------ ------------ -------------------------------------------------------DB_SHUTDOWN_TRIGGER TRIGGER CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."DB_SHUTDOWN_TRIGGER" BEFORE SHUTDOWN ON DATABASE BEGIN sqlarea_log; END; ALTER TRIGGER "TRIGGER_TEST"."DB_SHUTDOWN_TRIGGER" ENABLE

Page 23

DB_STARTUP_TRIGGER

TRIGGER

CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."DB_STARTUP_TRIGGER" AFTER STARTUP ON DATABASE BEGIN pin_objects; END; ALTER TRIGGER "TRIGGER_TEST"."DB_STARTUP_TRIGGER" ENABLE CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."LOGOFF_LOG_TRIGGER" BEFORE LOGOFF ON DATABASE BEGIN UPDATE session_logon_statistics SET end_time = SYSDATE WHERE sid = (select distinct sid from v$mystat) AND end_time IS NULL; END; ALTER TRIGGER "TRIGGER_TEST"."LOGOFF_LOG_TRIGGER" ENABLE CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."LOGON_LOG_TRIGGER" AFTER LOGON ON DATABASE BEGIN INSERT INTO session_logon_statistics (sid, user_logged, start_time) SELECT DISTINCT sid, USER, SYSDATE FROM v$mystat; END; ALTER TRIGGER "TRIGGER_TEST"."LOGON_LOG_TRIGGER" ENABLE CREATE OR REPLACE PROCEDURE "TRIGGER_TEST"."PIN_OBJECTS" (p_pin_flag_txt IN VARCHAR2 := 'P') IS -- The p_pin_flag_txt is either 'P' for pin -- or 'U' for unpin. CURSOR cur_pin_objects IS SELECT owner || '.' owner, object, type FROM objects_to_pin ORDER BY owner, object; BEGIN FOR cur_pin_objects_rec IN cur_pin_objects LOOP IF p_pin_flag_txt = 'U' THEN DBMS_SHARED_POOL.UNKEEP(cur_pin_objects_rec.owner || cur_pin_objects_rec.object, cur_pin_objects_rec.type); ELSE DBMS_SHARED_POOL.KEEP(cur_pin_objects_rec.owner || cur_pin_objects_rec.object, cur_pin_objects_rec.type); END IF; END LOOP; END pin_objects; CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."SOURCE_HISTORY" AFTER CREATE ON DATABASE BEGIN INSERT INTO source_history SELECT sysdate, owner, name, type, line, text FROM dba_source WHERE owner = ora_dict_obj_owner AND name = ora_dict_obj_name AND type = ora_dict_obj_type; END; ALTER TRIGGER "TRIGGER_TEST"."SOURCE_HISTORY" ENABLE

LOGOFF_LOG_TRIGGER

TRIGGER

LOGON_LOG_TRIGGER

TRIGGER

PIN_OBJECTS

PROCEDURE

SOURCE_HISTORY

TRIGGER

As can be seen in the output, the triggers are created and enabled. Only a subset of the output is shown above, and only a basic example of the use of the DBMS_METADATA package was shown above. This package is extremely powerful.

Page 24

SUMMARY
This paper outlined the new database triggers that were introduced in Oracle8i and enhanced each version to increase the power and flexibility of this database feature. The new triggers were outlined with examples to highlight the EXTREME flexibility and functionality available.

ABOUT

THE

AUTHOR

Joe Trezzo (trezzoj@tusc.com) is a cofounder, President and Chief Operating Officer of TUSC (www.tusc.com). He has been working with Oracle since version 4 and is the author of the Oracle Press book titled Oracle PL/SQL Tips and Techniques. He is a regular presenter at many Oracle user groups and is in the Entrepreneur Hall of Fame.

REFERENCES
Oracle PL/SQL Tips and Techniques (Oracle Press), Joseph C. Trezzo Oracle9i Instant PL/SQL Scripts (Oracle Press), Kevin Loney Oracle9i DBA Handbook (Oracle Press), Kevin Loney Oracle9i The Complete Reference (Oracle Press), Kevin Loney Oracle9i New Features (Oracle Press), Robert Freeman PL/SQL User's Guide and Reference(Release 9.0.1 & 9.2.0), Oracle Corporation Supplied PL/SQL Packages and Types Reference (Release 9.0.1 & 9.2.0), Oracle Corporation Oracle9i Database Administrators Guide (Release 9.0.1 & 9.2.0), Oracle Corporation Oracle9i SQL Reference (Release 9.0.1 & 9.2.0), Oracle Corporation Oracle9i Database Concepts (Release 9.0.1 & 9.2.0), Oracle Corporation Oracle9i Application Developers Guide - Fundamentals (Release 9.0.1 & 9.2.0), Oracle Corporation Oracle9i Database New Features (Release 9.0.1 & 9.2.0), Oracle Corporation $ORACLE_HOME/rdbms/doc/README_rdbms.htm www.tusc.com www.osborne.com

Neither TUSC nor the author guarantees this document to be error-free. Please provide comments/questions to trezzoj@tusc.com.

Page 25

You might also like