9i Database Triggers

Embed Size (px)

Citation preview

  • 8/2/2019 9i Database Triggers

    1/25

    NNEWEW OORACLERACLE99II DDATABASEATABASE TTRIGGERSRIGGERS

    Joe Trezzo, TUSC

    INTRODUCTIONOracle8i introduced an important expansion of database triggers that has been enhanced witheach new version. This important expansion extended the use of database triggers beyondtables to allow more flexibility and functionality to be integrated into your databaseenvironment and applications. From Oracle 8.1 to Oracle 9.2, this expansion has continued andthe power has increased.

    Most DBAs and developers are unaware or have not taken advantage of these new databasetriggers, 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. Thispaper concentrates on identifying the new database triggers and associated components ofthese 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

    New Database Triggers

    Database System Events

    DDL/Client Events

    Event Attributes

    Database Trigger Examples (DBA and Developer)

    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) Data Dictionary Views for Database Triggers

    Re-Creating OBJECT Creation (DBMS_METADATA Package)

    All examples were executed under Oracle 9.2.0.1.0.

    OVERVIEWOF DATABASE TRIGGERS

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

    Page 1

  • 8/2/2019 9i Database Triggers

    2/25

    the introduction of the internet, many companies applications are up and running 24X7X52 andare 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 placeto ensure that access is only allowed through the application and that activity in the database ismonitored.

    Prior to Oracle8i, Oracle provided the ability to build logic at the table level through databasetriggers. 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 ofdefense 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 databasestartup/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 thesenew triggers. Once the database trigger is created and enabled, it is executed based ondatabase 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 thatcreate 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 packagethat contains a variety of functions, including the event attribute functions.

    DBMSTRIG.SQL creates functions on top of the DBMS_STANDARD functions to allow for easyreference to the event attributes by granting EXECUTE on these functions and creating aPUBLIC synonym for these functions.

    A segment of the DBMSTRIG.SQL file is shown below for the ORA_DICT_OBJ_NAME eventattribute.

    Rem returns the object name on which the DDL statement is being done

    create or replace function dictionary_obj_name return varchar2 is

    beginreturn 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

    /

    CREATIONOF 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 (DATABASEschema.SCHEMA);

    DROP TRIGGER trigger_name;

    When a database trigger is created, the trigger is checked for syntax, the dependency tree andprivileges 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 isstored in a different data dictionary table and database triggers execute implicitly based onactions, whereas, packages and procedures are explicitly called.

    Page 2

  • 8/2/2019 9i Database Triggers

    3/25

    If errors occur during the creation or compilation of a database trigger, then the trigger is stillcreated 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, thetrigger needs to be either dropped, fixed and re-created, or disabled to ensure that processingdoes not stop. To view errors, the SHOW ERRORS command can be executed or the errors canbe 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 onlycreate a database trigger when truly needed

    Keep the length of the database trigger code relatively small, if it starts to get lengthy, createa stored procedure and call the procedure from the trigger; likewise if the segment of code inthe database trigger is used in other locations of code, make it modular and create a storedprocedure and call the procedure

    Only create a database trigger when needed to avoid unnecessary overhead, sincedependent 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 thebody of the trigger into a stored package or procedure.

    SECURITYOF 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 theyown.

    CREATE ANY TRIGGER: this privilege allows a schema to create a database trigger on a tableowned by another schema.

    ADMINISTER DATABASE TRIGGER: this privilege allows a schema to create a database widedatabase trigger.

    Once a trigger is created, it is executed implicitly. Internally, Oracle fires the trigger in theexisting user transaction. However, triggers execute in the same manner as the default of storedpackages 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 fromwithin 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 dependenciesthat can cause a trigger to become invalidated. Any time a referenced stored package orprocedure 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 astandard, a trigger that becomes invalidated, should be recompiled manually to ensure that thetrigger will compile successfully. To compile a trigger manually, the ALTER TRIGGER command isused. 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 systemprivilege. If a package or procedure referenced in a trigger is dropped, then the trigger becomes

    Page 3

  • 8/2/2019 9i Database Triggers

    4/25

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

    ENABLINGAND DISABLING DATABASE TRIGGERS

    Disabled database triggers are companions to invalid objects. In some respects, a disabledtrigger is far more dangerous than an invalid object because it doesnt fail; it just doesntexecute! This can have severe consequences for applications (and, consequently, for businessprocesses) that depend on business logic stored within procedural code in database triggers. Forthis reason, you MUST run the following script regularly to ensure there are not any disabledtriggers 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/SQLscript can be created to build the SQL statements to ENABLE the triggers. To enable databasetriggers, the following three commands could be executed.

    ALTER TRIGGER db_startup_trigger ENABLE; -- enabling a database trigger

    ALTER TRIGGER before_insert_customer ENABLE; -- enabling a table trigger

    ALTER TABLE s_customer ENABLE ALL TRIGGERS; -- 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 scriptdynamically:

    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 todynamically build a DISABLE script.

    There may be times that you want to disable triggers for a data load or special processing. Inthis 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

  • 8/2/2019 9i Database Triggers

    5/25

    Prior to version 7.3 of Oracle (version 2.3 of PL/SQL), triggers were not stored in compiledformat. This means every time the trigger was executed, the trigger was compiled and loadedinto 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 toFALSE to disable event triggers. In Oracle8i, the parameter is not hidden, and issystem_trig_enabled. This parameter can be used during an upgrade, downgrade and/or whenpatching applications.

    NEW DATABASE TRIGGERS

    The 20 new triggers are broken into two main categories by Oracle, namely, database systemevents and DDL/client events. For each event, there are event attributes set internally by Oraclewhen the event takes place. These event attributes can be referenced in the database triggerlogic. 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 areoutlined below, along with a description and the event attributes that are set for each event.

    DatabaseTrigger

    BEFORE/AFTER Execution Description Attribute Event

    LOGOFF BEFORE Executed when a user logs

    off, at the start of the

    logoff process

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    LOGON AFTER Executed when a user logs

    into the database, after a

    successful login of the user

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_client_ip_address

    STARTUP AFTER Executed when the database is

    opened; starts a separate

    transaction and commits after

    this trigger is complete

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    SHUTDOWN BEFORE Executed when the instance is

    shutdown; prior to the

    shutdown of the instance

    process; not always executed

    on abnormal shutdown; startsa separate transaction and

    commits after this trigger is

    complete

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    Page 5

  • 8/2/2019 9i Database Triggers

    6/25

    SERVERERROR 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

    ora_syseventora_login_userora_instance_numora_database_nameora_server_error

    ora_is_servererrorspace_error_info

    SUSPEND AFTER Executed whenever a server

    error causes a transaction to

    be suspended (example: out-

    of-space error)

    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 fourdatabase system events can be created at the database or schema levels. The STARTUP triggerreturns 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 willexecute for all schemas, or these can be created at the schema level and will execute only forthe schema it is created for. When a trigger is created at the schema level, the trigger is createdin the schema specified and executes only for that schema.

    This provides a great deal of flexibility depending on your environment and what you want tomonitor or respond to. The 14 DDL/client event triggers are outlined below, along with adescription and the event attributes that are set for each event.

    Database

    Trigger

    BEFORE/AFTE

    R Execution Description Attribute Events

    Page 6

  • 8/2/2019 9i Database Triggers

    7/25

    ALTERBEFORE/AFTER Executed when object

    altered

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_type

    ora_dict_obj_name

    ora_dict_obj_owner

    ora_des_encrypted_password

    (for ALTER USER events)

    ora_is_alter_column,

    ora_is_drop_column (for

    ALTER TABLE events)

    DROP BEFORE/AFTER Executed when object is

    dropped

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_type

    ora_dict_obj_name

    ora_dict_obj_owner

    ANALYZE BEFORE/AFTER Executed when the analyze

    command is executed

    ora_syseventora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_name

    ora_dict_obj_type

    ora_dict_obj_owner

    ASSOCIATE

    STATISTICS

    BEFORE/AFTER Executed when the associate

    statistics command is

    executed

    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_listora_dict_obj_owner_list

    AUDIT/NOAUDIT BEFORE/AFTER Executed when the audit or

    noaudit command is executed

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    COMMENT BEFORE/AFTER Executed when the comment

    command is executed

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_name

    ora_dict_obj_type

    ora_dict_obj_owner

    CREATE BEFORE/AFTER Executed when an object is

    created

    ora_syseventora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_type

    ora_dict_obj_name

    ora_dict_obj_owner

    ora_is_creating_nested_table

    (for CREATE TABLE events)

    Page 7

  • 8/2/2019 9i Database Triggers

    8/25

    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)

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_name

    ora_dict_obj_type

    ora_dict_obj_owner

    DISASSOCIATE

    STATISTICS

    BEFORE/AFTER Executed when the

    disassociate statistics

    command is executed

    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

    GRANT BEFORE/AFTER Executed when the grant

    command is executed

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_nameora_dict_obj_name

    ora_dict_obj_type

    ora_dict_obj_owner

    ora_grantee

    ora_with_grant_option

    ora_privileges

    RENAME BEFORE/AFTER Executed when the rename

    command is executed

    ora_sysevent

    ora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_name

    ora_dict_obj_owner

    ora_dict_obj_type

    REVOKE BEFORE/AFTER Executed when the revoke

    command is executed

    ora_syseventora_login_user

    ora_instance_num

    ora_database_name

    ora_dict_obj_name

    ora_dict_obj_type

    ora_dict_obj_owner

    ora_revokee

    ora_privileges

    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

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

    Page 8

  • 8/2/2019 9i Database Triggers

    9/25

    through the LOGON and LOGOFF triggers. An object audit trail can be created through theCREATE, 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 sectionhighlighted each of the database triggers, along with the event attributes that are set and canbe referenced for each trigger. Below is a list of each of the attribute events, the data type and ashort description.

    Attribute Event Data Type Description

    ora_client_ip_address VARCHAR2Provides the IP address of the clientmachine when using TCP/IP

    ora_database_name VARCHAR2(50)Provides the database name

    ora_des_encrypted_password VARCHAR2

    Provides the DES encrypted password of

    the user being created or altered

    ora_dict_obj_name VARCHAR(30)Provides the object name of the objectbeing manipulated

    ora_dict_obj_name_list(name_list OUTora_name_list_t)

    BINARY_INTEGERProvides a list of object names beingmanipulated

    ora_dict_obj_owner VARCHAR(30)Provides the owner of the object beingmanipulated

    ora_dict_obj_owner_list(owner_list OUT ora_name_list_t)

    BINARY_INTEGERProvides the owners of the objects beingmanipulated

    ora_dict_obj_type VARCHAR(20) Provides the type of object beingmanipulated

    ora_grantee(user_listOUT ora_name_list_t)

    BINARY_INTEGER Provides the number of grantees

    ora_instance_num NUMBER Provides the instance number.

    ora_is_alter_column(column_name IN VARCHAR2)

    BOOLEANProvides a return value of TRUE if thespecified column is altered

    ora_is_creating_nested_table BOOLEANProvides a return value of TRUE if thecurrent event is creating a nested table

    ora_is_drop_column(

    column_name IN VARCHAR2) BOOLEAN

    Provides a return value of TRUE if the

    specified column is dropped

    ora_is_servererror BOOLEANProvides a return value of TRUE is the errorspecified is on the error stack

    ora_login_user VARCHAR2(30) Provides the login schema

    ora_partition_pos BINARY_INTEGERProvides the position in a CREATE TABLEcommand where the partition clause can beinserted when using the INSTEAD OF trigger

    ora_privilege_list(privilege_list OUT

    BINARY_INTEGER Provides the list of privileges being granted

    Page 9

  • 8/2/2019 9i Database Triggers

    10/25

    ora_name_list_t) or revoked

    ora_revokee (user_list OUTora_name_list_t)

    BINARY_INTEGERProvides a list of the revokees of the revokecommand

    ora_server_error NUMBERProvides the error on the error stack for theposition specified in the stack (1 meaningthe top of the stack)

    ora_server_error_depth BINARY_INTEGERProvides the total number of errors on theerror stack

    ora_server_error_msg(position in binary_integer)

    VARCHAR2Provides the error on the error stack for theposition specified in the stack (1 meaningthe top of the stack)

    ora_server_error_num_params(position in binary_integer)

    BINARY_INTEGER

    Provides the number of strings that havebeen substituted into the error message onthe error stack for the position specified in

    the stack (1 meaning the top of the stack)

    ora_server_error_param(position in binary_integer,param in binary_integer)

    VARCHAR2

    Provides the matching substitution value inthe error message for the parameternumber specified in conjunction with theposition specified on the stack ( 1 meaningthe top of the stack)

    ora_sql_txt (sql_text outora_name_list_t)

    BINARY_INTEGER

    Provides the SQL statement of thestatement that caused the trigger toexecute (if the statement is lengthy, it willseparate it into multiple PL/SQL tableelements); the value returned specifies the

    number of elements

    ora_sysevent VARCHAR2(20)Provides the system or client event thatcaused the trigger to execute

    ora_with_grant_option BOOLEANProvides a return value of TRUE if theprivileges are granted with the grant option

    Space_error_info(error_number OUT NUMBER,error_type OUT VARCHAR2,object_owner OUT VARCHAR2,table_space_name OUTVARCHAR2,object_name OUT VARCHAR2,

    sub_object_name OUTVARCHAR2)

    BOOLEAN

    Provides a return value of true if the error isrelated to an out-of-space error andprovides the object information of theobject with the error

    These attribute events allow extreme flexibility and functionality in each of the database triggersand 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 avariety of examples to illustrate this power. A list of the examples is provided below.

    Logging Connection Time (LOGON/LOGOFF)

    Page 10

  • 8/2/2019 9i Database Triggers

    11/25

    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 databasetrigger to capture the time when a user connects/disconnects to/from the database.

    CREATE TABLE session_logon_statistics

    (sid NUMBER,

    user_logged VARCHAR2(30),

    start_time DATE,end_time 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 DATABASEBEGIN

    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 theV_$MYSTAT view to the creator of these triggers. The following script retrieves the informationfrom the SESSION_LOGON_STATISTICS table.

    COLUMN user_logged FORMAT a15COLUMN start_time FORMAT a20

    COLUMN end_time FORMAT a20

    SELECT 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

  • 8/2/2019 9i Database Triggers

    12/25

    12 TRIGGER_TEST 01/22/2003 19:17:24 01/22/2003 19:17:46

    13 PLSQL_USER 01/22/2003 19:12:19 01/22/2003 19:18:13

    13 SYS 01/22/2003 19:18:38 01/22/2003 19:19:34

    13 SYS 01/22/2003 19:19:35 01/22/2003 19:19:53

    13 SYS 01/22/2003 19:19:59

    14 TRIGGER_TEST 01/22/2003 19:12:29 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. Itprovides a dynamic and flexible method to control the pinning by merely inserting and deletingfrom a database table.

    Because Oracle uses an LRU algorithm for the caching of objects in the Shared Pool, objects canbe flushed out of the Shared Pool. If the objects are large, this will cause degradation inperformance and possible errors because objects are loaded into the Shared Pool in contiguoussegments. Heavily executed or large and important stored PL/SQL program units should becached or pinned in the Shared Pool. Stored PL/SQL program units get pinned in the object cacheand cursors get pinned in the SQL Area.

    Oracle provides a procedure in the DBMS_SHARED_POOL package to pin these objects. This isthe only method of pinning these objects in the shared pool and this package is not created bydefault. The dbmspool.sql script must be executed under the SYS schema to create this packageBy 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 orunpinned 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 unitsto pin. This should be created under a user with DBA privilege.

    CREATE TABLE objects_to_pin

    (owner VARCHAR2(30) NOT NULL,

    object VARCHAR2(128) NOT NULL,type VARCHAR2(1) NOT NULL);

    The following is a basic method of inserting objects to pin into the table. This can be modified tobe 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 includedfor reference below:

    Value Kind of Object to keep

    ----- ----------------------

    P package/procedure/function

    Q sequence

    R trigger

    T type

    JS java source

    JC java class

    Page 12

  • 8/2/2019 9i Database Triggers

    13/25

    JR java resource

    JD java shared data

    C 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 thetable.

    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 thePL/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 databasestartup script to the new STARTUP database trigger.

    The STANDARD package and SOURCE_HISTORY were insert into the OBJECTS_TO_PIN table usingthe INSERT script above. The following output displays the contents of this table.

    COLUMN object FORMAT a30

    SELECT *

    FROM objects_to_pin;

    OWNER OBJECT TYPE

    --------------- ------------------------------ ------------

    SYS STANDARD P

    TRIGGER_TEST SOURCE_HISTORY R

    The following query displays the stored PL/SQL program units information regarding pinning. Thescript below has been modified to focus on the two objects that we are attempting to pin. TheWHERE 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

  • 8/2/2019 9i Database Triggers

    14/25

    COLUMN type FORMAT a12

    SET PAGESIZE 58

    SELECT owner, name, type, kept

    FROM v$db_object_cache

    WHERE name in ('STANDARD', 'SOURCE_HISTORY');

    OWNER NAME TYPE KEP

    --------------- ------------------------- ------------ ---

    SYS STANDARD PACKAGE NO

    SYS STANDARD PACKAGE BODY NO

    TRIGGER_TEST SOURCE_HISTORY TABLE NO

    TRIGGER_TEST SOURCE_HISTORY TRIGGER 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 andexecutions columns) to determine which objects should be pinned.

    In order to take this example full circle, a database startup script is created that calls thePIN_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_CACHEview 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 135338868 bytes

    Fixed Size 453492 bytes

    Variable Size 109051904 bytes

    Database Buffers 25165824 bytes

    Redo Buffers 667648 bytes

    Database mounted.

    Database opened.

    OWNER NAME TYPE KEP

    --------------- ------------------------- ------------ ---

    SYS STANDARD PACKAGE YES

    SYS STANDARD PACKAGE BODY YES

    TRIGGER_TEST SOURCE_HISTORY TABLE NO

    TRIGGER_TEST SOURCE_HISTORY TRIGGER YES

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

    Page 14

  • 8/2/2019 9i Database Triggers

    15/25

    AUDIT TRAILOF OBJECTS (CREATE/ALTER/DROP)

    The next example provides the ability to build your own flexible object audit trail. The examplebelow creates database triggers to capture and log every CREATE, ALTER and DROP operationon 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 linewould be changed to ON TRIGGER_TEST.SCHEMA where TRIGGER _TEST is the name of theschema 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

  • 8/2/2019 9i Database Triggers

    16/25

    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 belogged to the AUDIT_OBJECT_MODS table. To view the audit, the following script can beexecuted.

    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 a25SET 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 TYPE_OF_MO MOD_USER I_NUM DATABASE_N OBJECT_OWNER OBJECT_TYP OBJECT_NAME

    --------- ---------- ------------ ----- ---------- ------------ ---------- -------------------------

    27-JAN-03 CREATE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER ALTER_OBJECT_TRIGGER27-JAN-03 CREATE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER DROP_OBJECT_TRIGGER

    27-JAN-03 CREATE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST PROCEDURE PIN_OBJECTS

    27-JAN-03 CREATE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER DB_STARTUP_TRIGGER

    27-JAN-03 DROP TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER DATABASE_STARTUP_TRIGGER27-JAN-03 CREATE TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TABLE TEST14

    27-JAN-03 ALTER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER DB_STARTUP_TRIGGER27-JAN-03 ALTER TRIGGER_TEST 1 TUSC.WORLD TRIGGER_TEST TRIGGER DB_STARTUP_TRIGGER

    The individual CREATE, ALTER, and DROP database triggers created previously can be replacedwith 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;

    /

    DISABLETHE ABILITYTO DROP OBJECTS (DROP)

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

    Page 16

  • 8/2/2019 9i Database Triggers

    17/25

    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 theirresponsibilities while the database is up and running, thus reducing the time of off-hoursadministration. With database triggers, the logon trigger allows a DBA to disable new logons bysetting 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 toreference this table upon logon and look for the value of the record to determine if logins areallowed.

    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

  • 8/2/2019 9i Database Triggers

    18/25

    END;

    /

    The trigger looks for a value of YES and if the value is NOT YES, then the login will not succeedand 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 fromerrors in the logon triggers and will be allowed to logon. Likewise, any schema with ADMINISTERDATABASE TRIGGER system privilege follows the same logic. Therefore, when the creator of thetrigger above (TRIGGER_TEST who has the above privilege), as well as the SYS and SYSTEMusers attempt to logon, they succeed. If the trigger was created with an error and is INVALID, the3 users and any schema with the privilege above would still succeed upon logon. However, if thetrigger was created with an error and is INVALID and anyone else attempts to logon, they willreceive 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 ofadvantages, however, the biggest disadvantage is that developers often times bypass versioncontrol mechanisms in place to make a quick fix and forget to update the main source codeversion of the PL/SQL object that resides outside of the database. The problem comes in whenthe next change is made the correct way by modifying the code outside the database and thenrecreating it in the database. The prior change made in the database is now lost. I have heardabout this many times.

    The new triggers provide a mechanism to help this problem and create an audit trail/version ofall PL/SQL source code created in the database. This is accomplished by inserting the newsource code for an object into a history table each time an object is created or recreated. It isaccomplished behind the scenes with the CREATE trigger. Each of the creations is alsotimestamped with the creation date.

    A history table is first created that mirrors the DBA_SOURCE table as shown below.

    Page 18

  • 8/2/2019 9i Database Triggers

    19/25

    CREATE TABLE source_history

    (change_date DATE NOT NULL,

    owner VARCHAR2(30) NOT NULL,

    name VARCHAR2(30) NOT NULL,

    type VARCHAR2(20),line NUMBER NOT NULL,

    text VARCHAR2(4000));

    A CREATE trigger is then created that will insert into the SOURCE_HISTORY table after each newobject 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_OWNERAND 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 onlychanging the view name to SOURCE_HISTORY. A sample script is shown below with the querylimited to one object.

    COLUMN owner FORMAT a12

    COLUMN name FORMAT a11

    COLUMN line FORMAT 9999

    COLUMN text FORMAT a60 WORD_WRAPPEDSELECT change_date, owner, name, type, line, text

    FROM source_history

    WHERE name = 'PIN_OBJECTS'

    order by change_date, owner, name, type, line;

    CHANGE_DA OWNER NAME TYPE LINE TEXT

    --------- ------------ ----------- ---------- ----- ------------------------------------------------------------

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 1 PROCEDURE pin_objects27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 2 (p_pin_flag_txt IN VARCHAR2 := 'P') IS

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 3 -- The p_pin_flag_txt is either 'P' for pin

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 4 -- or 'U' for unpin.

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 5 CURSOR cur_pin_objects IS27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 6 SELECT owner || '.' owner,

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 7 object

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 8 FROM objects_to_pin

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 9 ORDER BY owner, object;27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 10 BEGIN

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 11 FOR cur_pin_objects_rec IN cur_pin_objects LOOP

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 12 IF p_pin_flag_txt = 'U' THEN

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 13 DBMS_SHARED_POOL.UNKEEP(cur_pin_objects_rec.owner ||27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 14 cur_pin_objects_rec.object, 'P');

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 15 ELSE27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 16 DBMS_SHARED_POOL.KEEP(cur_pin_objects_rec.owner ||

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 17 cur_pin_objects_rec.object, 'P');27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 18 END IF;

    27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 19 END LOOP;27-JAN-03 TRIGGER_TEST PIN_OBJECTS PROCEDURE 20 END pin_objects;

    This query can also be changed to include the time .

    Page 19

  • 8/2/2019 9i Database Triggers

    20/25

    LOG SQL INFORMATION UPON SHUTDOWN (SHUTDOWN)

    The following illustrates a method of logging information when the database is shutdown. Thiscan be extended to log more information or perform certain operations. The example logsinformation 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 alow threshold in the example below for the number of READS. This can be modified for yoursystem 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

  • 8/2/2019 9i Database Triggers

    21/25

    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.name),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 DICTIONARYVIEWSFOR DATABASE TRIGGERS

    There are several data dictionary views that reveal information about triggers. The main twoviews center on the source code view that contains the source code for package, procedures andfunctions, but also stores the compilation status of triggers. This is important to determine if atrigger has been compiled successfully. The source code for triggers and the importantinformation regarding being ENABLED or DISABLED is also stored in the trigger views. There are3 views for each that center on the scope of the information. These are listed below.

    user_triggers user_sourceall_triggers all_sourcedba_triggers dba_source

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

    Page 21

  • 8/2/2019 9i Database Triggers

    22/25

    not, then changes can be made until it does become VALID. You always want to know prior toproduction if there will be compilation issues that you need to address.

    COLUMN object_name FORMAT a24

    SELECT object_name, object_type, status

    FROM user_objects

    WHERE object_type = 'TRIGGER';

    OBJECT_NAME OBJECT_TYP STATUS

    ------------------------ ---------- -------

    ALTER_OBJECT_TRIGGER TRIGGER VALID

    CREATE_OBJECT_TRIGGER TRIGGER VALID

    DB_SHUTDOWN_TRIGGER TRIGGER VALID

    DB_STARTUP_TRIGGER TRIGGER VALID

    DROP_OBJECT_TRIGGER TRIGGER VALID

    LOGOFF_LOG_TRIGGER TRIGGER VALID

    LOGON_ALLOW_TRIGGER TRIGGER VALID

    LOGON_LOG_TRIGGER TRIGGER VALID

    SOURCE_HISTORY TRIGGER VALIDSTOP_DROP_TRIGGER TRIGGER VALID

    TEST TRIGGER VALID

    TEST_LOGON TRIGGER VALID

    As evidenced in the output, all triggers are VALID at the current time. If an object that isreferenced by one of these triggers is dropped or altered in any manner, this will cause thetrigger to become INVALID.

    The second example provides information about the triggers in the system. This is useful tounderstand the database and schema triggers that are defined in a database.

    COLUMN trigger_name FORMAT a24

    COLUMN triggering_event FORMAT a15SELECT trigger_name, trigger_type, base_object_type, triggering_event, status

    FROM user_triggers

    WHERE db_object_type IN ('DATABASE ', 'SCHEMA');

    TRIGGER_NAME TRIGGER_TYPE BASE_OBJECT_TYPE TRIGGERING_EVEN STATUS

    ------------------------ ---------------- ---------------- --------------- --------

    ALTER_OBJECT_TRIGGER AFTER EVENT DATABASE ALTER ENABLED

    CREATE_OBJECT_TRIGGER AFTER EVENT DATABASE CREATE ENABLED

    DB_SHUTDOWN_TRIGGER BEFORE EVENT DATABASE SHUTDOWN ENABLED

    DB_STARTUP_TRIGGER AFTER EVENT DATABASE STARTUP ENABLED

    DROP_OBJECT_TRIGGER AFTER EVENT DATABASE DROP ENABLED

    LOGOFF_LOG_TRIGGER BEFORE EVENT DATABASE LOGOFF ENABLED

    LOGON_ALLOW_TRIGGER AFTER EVENT DATABASE LOGON ENABLED

    LOGON_LOG_TRIGGER AFTER EVENT DATABASE LOGON ENABLEDSOURCE_HISTORY AFTER EVENT DATABASE CREATE ENABLED

    TEST_LOGON AFTER EVENT DATABASE LOGON ENABLED

    STOP_DROP_TRIGGER BEFORE EVENT SCHEMA DROP ENABLED

    TEST AFTER EVENT SCHEMA LOGON ENABLED

    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 onENABLING and DISABLING triggers for more information on this view.

    Page 22

  • 8/2/2019 9i Database Triggers

    23/25

    The last example provides the source code of each of the triggers (limited to theDB_STARTUP_TRIGGER trigger).

    COLUMN trigger_name FORMAT a24

    COLUMN triggering_event FORMAT a15

    COLUMN trigger_body FORMAT a25 word_wrappedSELECT trigger_name, trigger_type, base_object_type, triggering_event, trigger_body

    FROM user_triggers

    WHERE 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 objectcreation layer. This package provides a variety of procedures and functions (19 total) to allowmanipulation of this information. We will concentrate on the GET_DDL function that allows theobject 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 CLOBArgument Name Type In/Out Default?

    ------------------------------ ----------------------- ------ --------OBJECT_TYPE VARCHAR2 INNAME VARCHAR2 INSCHEMA VARCHAR2 IN DEFAULTVERSION VARCHAR2 IN DEFAULTMODEL VARCHAR2 IN DEFAULTTRANSFORM VARCHAR2 IN DEFAULT

    The creation statements of the procedures and triggers created in this paper can be retrievedvia the DBMS_METADATA.GET_DDL function in the following code segment. The output isdisplayed to the screen, but could be directed to a flat file via the UTL_FILE package.

    SET LONG 1000SELECT 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

    BEGINsqlarea_log;

    END;

    ALTER TRIGGER "TRIGGER_TEST"."DB_SHUTDOWN_TRIGGER" ENABLE

    Page 23

  • 8/2/2019 9i Database Triggers

    24/25

    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

    LOGOFF_LOG_TRIGGER TRIGGER CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."LOGOFF_LOG_TRIGGER"BEFORE LOGOFF ON DATABASE

    BEGIN

    UPDATE session_logon_statistics

    SET end_time = SYSDATEWHERE sid = (select distinct sid from v$mystat)

    AND end_time IS NULL;

    END;

    ALTER TRIGGER "TRIGGER_TEST"."LOGOFF_LOG_TRIGGER" ENABLE

    LOGON_LOG_TRIGGER TRIGGER CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."LOGON_LOG_TRIGGER"

    AFTER LOGON ON DATABASEBEGIN

    INSERT INTO session_logon_statistics

    (sid, user_logged, start_time)

    SELECT DISTINCT sid, USER, SYSDATEFROM v$mystat;

    END;

    ALTER TRIGGER "TRIGGER_TEST"."LOGON_LOG_TRIGGER" ENABLE

    PIN_OBJECTS PROCEDURE 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, typeFROM objects_to_pin

    ORDER BY owner, object;BEGIN

    FOR cur_pin_objects_rec IN cur_pin_objects LOOPIF 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);

    ELSEDBMS_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;

    SOURCE_HISTORY TRIGGER CREATE OR REPLACE TRIGGER "TRIGGER_TEST"."SOURCE_HISTORY"

    AFTER CREATE ON DATABASEBEGIN

    INSERT INTO source_historySELECT sysdate, owner, name, type, line, text

    FROM dba_sourceWHERE 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

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

    Page 24

  • 8/2/2019 9i Database Triggers

    25/25

    SUMMARY

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

    ABOUTTHE AUTHOR

    Joe Trezzo ([email protected]) 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 theOracle Press book titled Oracle PL/SQL Tips and Techniques. He is a regular presenter at manyOracle 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), OracleCorporation

    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 providecomments/questions to [email protected].

    http://www.tusc.com/http://www.osborne.com/mailto:[email protected]://www.tusc.com/http://www.osborne.com/mailto:[email protected]