trigger AFTER SUSPEND for tablspace quota

1) trigger AFTER SUSPEND for tablspace quota
1@@@@Setting Up for the AFTER SUSPEND Trigger
Thankfully, an AFTER SUSPEND trigger can eliminate the dark circles under both
Don¡¯s and Batch¡¯s eyes. Here is how they work through the situation.
Batch discovers a particular point in his code that encounters the error most frequently.
It is an otherwise innocuous INSERT statement at the end of a program that takes hours
to run:
    INSERT INTO monthly_summary (
       acct_no, trx_count, total_in, total_out)
    VALUES (
       v_acct, v_trx_count, v_total_in, v_total_out);

What makes this most maddening is that the values take hours to calculate, only to be
immediately lost when the final INSERT statement fails. At the very least, Batch wants
the program to suspend itself while he contacts Don to get more space allocated. He
discovers that this can be done with a simple ALTER SESSION statement.
    ALTER SESSION ENABLE RESUMABLE TIMEOUT 3600 NAME 'Monthly Summary';

This means that whenever this Oracle database session encounters an out-of-space
error, it will go into a suspended (and potentially resumable) state for 3,600 seconds
(1 hour). This provides enough time for Totally¡¯s monitoring system to page Batch,

Batch to phone Don, and for Don to allocate more space. It¡¯s not a perfect system, but
at least the hours spent calculating the data are no longer wasted.
  The RESUMABLE system privilege must be granted to users before they
  can enable the resumable option.

Now, whenever Batch¡¯s programs go into the suspended state, he only has to phone
Don and mumble ¡°Check the resumable view.¡± Don then queries it from his DBA
account to see what is going on.
    SQL> SELECT session_id,
     2          name,
     3          status,
     4          error_number
     5     FROM dba_resumable
    SESSION_ID NAME STATUS ERROR_NUMBER
    ---------- -------------------- --------- ------------
    8 Monthly Summary SUSPENDED 1536
    1 row selected.
This shows that session 8 is suspended because of ORA-01536: space quota exceeded
for tablespace ¡®tablespace_name¡¯. From past experience, Don knows which schema and
tablespace are involved, so he corrects the problem and mumbles into the phone, ¡°It¡¯s
fixed.¡± The suspended statement in Batch¡¯s code immediately resumes, and both Don
and Batch can go back to sleep in their own beds.


Note: Invalid DDL Operation in System Triggers
   AFTER SUSPEND triggers are not allowed to actually perform certain DDL (ALTER
   USER and ALTER TABLESPACE) to fix the problems they diagnose. They simply raise
   the error ORA-30511: Invalid DDL operation in system triggers. One way to work
   around this situation is as follows:
    1. Have the AFTER SUSPEND trigger write the SQL statement necessary to fix a
       problem in a table.
    2. Create a PL/SQL package that reads SQL statements from the table and executes
       them.
    3. Submit the PL/SQL package to DBMS_JOB every minute or so.


Looking at the Actual Trigger
After a few weeks, both Don and Batch are tired of their repetitive, albeit abbreviated
late-night conversations, so Don sets out to automate things with an AFTER SUSPEND
trigger. Here¡¯s a snippet of what he cooks up and installs in the DBA account:


    @@@Example: Create a table and a package to capture a suspend info and NDS
    SYS@ocm> !cat fixer.sql
    DROP TABLE stuff_to_fix
    /
    CREATE TABLE stuff_to_fix
        (  stuff  VARCHAR2(1000)
         , fixed  VARCHAR2(1)  )
    /
    CREATE OR REPLACE PACKAGE fixer
    IS
      
      /* pass dynamic sql to this procedure, then save to
         to a table(stuff_to_fix) */
      PROCEDURE fix_this
        (  p_thing_to_fix_in  IN  VARCHAR2 );
      /* run the dynmaic sql which in stuff_to_fix table */
      PROCEDURE fix_stuff;
    END;
    /
    CREATE OR REPLACE PACKAGE BODY fixer
    IS
   
       PROCEDURE fix_this
        ( p_thing_to_fix_in  IN VARCHAR2 )
       IS
          PRAGMA AUTONOMOUS_TRANSACTION;
       BEGIN
          INSERT INTO stuff_to_fix( stuff , fixed )
          VALUES (  p_thing_to_fix_in , 'N'   );
          COMMIT;
       END fix_this;
   
   
       PROCEDURE fix_stuff
       IS
          -- update data by rowid after using cursor get the rows
          CURSOR curs_get_stuff_to_fix
          IS
           SELECT stuff, ROWID
             FROM stuff_to_fix 
            WHERE fixed = 'N';
       BEGIN
          FOR rec IN curs_get_stuff_to_fix
          LOOP
             EXECUTE IMMEDIATE rec.stuff;
             UPDATE stuff_to_fix
                SET fixed = 'Y'
              WHERE ROWID = rec.rowid;
          END LOOP;
          COMMIT;
       END;
    END;
    /
    SYS@ocm> @fixer.sql
    Table dropped.
    Table created.
    Package created.
    Package body created.
   
   
    @@@Create trigger use package above, use sys to create the package only,
       Note: schema would use local trigger if there are the same after suspend.
    SYS@ocm> !cat tmp.sql
    CREATE OR REPLACE TRIGGER after_suspend
      AFTER SUSPEND
      ON DATABASE
    DECLARE
       -- cursor to get the username for current session
       CURSOR curs_get_username
       IS
        SELECT username --return single row single column
          FROM v$session
         WHERE audsid = SYS_CONTEXT('USERENV','SESSIONID');
       gv_username VARCHAR2(30);
   
       -- cursor to get the quota for the user/tablespace
       CURSOR curs_get_ts_quota
            (  cp_tbspc_in IN VARCHAR2
             , cp_user_in IN VARCHAR2 )
       IS
        SELECT max_bytes --return single row single column
          FROM dba_ts_quotas
         WHERE tablespace_name = cp_tbspc_in
           AND username = cp_user_in;
       gv_old_quota  NUMBER;
       gv_new_quota  NUMBER;
        
       -- hold information form SPACE_ERROR_INFO
       gv_error_type        VARCHAR2(30);
       gv_object_type       VARCHAR2(30);
       gv_object_owner      VARCHAR2(30);
       gv_tbspc_name        VARCHAR2(30);
       gv_object_name       VARCHAR2(30);
       gv_subobject_name    VARCHAR2(30);
   
       -- SQL to fix things
       gv_sql    VARCHAR2(1000);
   
    BEGIN
     
       -- if this is a space related error...
       IF ORA_SPACE_ERROR_INFO
        (  error_type   => gv_error_type
         , object_type  => gv_object_type
         , object_owner => gv_object_owner
         , table_space_name => gv_tbspc_name
         , object_name      => gv_object_name
         , sub_object_name  => gv_subobject_name ) THEN
   
          -- if the error is a tablespace quota being exceeded...
          IF gv_error_type  = 'SPACE QUOTA EXCEEDED' AND
             gv_object_type = 'TABLE SPACE' THEN
   
             -- get the username
             OPEN  curs_get_username;
             FETCH curs_get_username INTO gv_username;
             CLOSE curs_get_username;
           
             -- get the current quota
             OPEN  curs_get_ts_quota(  gv_object_name
                                     , gv_username );
             FETCH curs_get_ts_quota INTO gv_old_quota;
             CLOSE curs_get_ts_quota;
   
             -- create an ALTER USER statement and send if off to the fixer job
             -- because if we try it here we will raise
             -- ORA-30511: invalid DDL operation in system triggers
             gv_new_quota := gv_old_quota + 40960;
             gv_sql :=  'ALTER USER' || gv_username
                     || ' QUOTA '    || gv_new_quota
                     || ' ON '       || gv_object_name;
           
             -- run procedure fix_this in  the package fixer
             fixer.fix_this(gv_sql);
          END IF;
       END IF;
    END;
    /
    SYS@ocm> @tmp.sql
    Trigger created.
   
    @@@Example: Create problem
      prepare:
    SYS@ocm> CREATE TABLESPACE test_suspend datafile '/u01/test.dbf' size 1M;
    SYS@ocm> ALTER USER hr DEFAULT TABLESPACE test_suspend;
    SYS@ocm> ALTER USER hr QUOTA 1M ON test_suspend;
    SYS@ocm> GRANT SELECT ON dba_source TO hr;
    SYS@ocm> GRANT RESUMABLE TO hr;
      begin:
    HR@ocm> ALTER SESSION ENABLE RESUMABLE TIMEOUT 3600 NAME 'Monthly Summary';
    HR@ocm> CREATE TABLE tab_test_suspend AS SELECT * FROM dba_source;
    CREATE TABLE tab_test_suspendx AS SELECT * FROM dba_source
                                                    *
    ERROR at line 1:
    ORA-30032: the suspended (resumable) statement has timed out
    ORA-01536: space quota exceeded for tablespace 'TEST_SUSPEND'
    YS@ocm> col stuff for a50
    SYS@ocm> select * from stuff_to_fix;
    STUFF                           F
    -------------------------------------------------- -
    ALTER USER HR QUOTA 21012480 ON TEST_SUSPEND       N




2@@@@Example02: use DBMS_RESUMABLE.abort to cancel user hanging
SYS@ocm> !cat tmp.sql
    CREATE OR REPLACE TRIGGER after_suspend
      AFTER SUSPEND
      ON DATABASE
    DECLARE
       -- cursor to get the username for current session
       CURSOR curs_get_username
       IS
        SELECT username, sid --return single row single column
          FROM v$session
         WHERE audsid = SYS_CONTEXT('USERENV','SESSIONID');
       gv_username VARCHAR2(30);
           gv_sid      NUMBER;
   
       -- cursor to get the quota for the user/tablespace
       CURSOR curs_get_ts_quota
            (  cp_tbspc_in IN VARCHAR2
             , cp_user_in IN VARCHAR2 )
       IS
        SELECT max_bytes --return single row single column
          FROM dba_ts_quotas
         WHERE tablespace_name = cp_tbspc_in
           AND username = cp_user_in;
       gv_old_quota  NUMBER;
       gv_new_quota  NUMBER;
        
       -- hold information form SPACE_ERROR_INFO
       gv_error_type        VARCHAR2(30);
       gv_object_type       VARCHAR2(30);
       gv_object_owner      VARCHAR2(30);
       gv_tbspc_name        VARCHAR2(30);
       gv_object_name       VARCHAR2(30);
       gv_subobject_name    VARCHAR2(30);
   
       -- SQL to fix things
       gv_sql    VARCHAR2(1000);
   
    BEGIN
     
       -- if this is a space related error...
       IF ORA_SPACE_ERROR_INFO
        (  error_type   => gv_error_type
         , object_type  => gv_object_type
         , object_owner => gv_object_owner
         , table_space_name => gv_tbspc_name
         , object_name      => gv_object_name
         , sub_object_name  => gv_subobject_name ) THEN
   
          -- if the error is a tablespace quota being exceeded...
          IF gv_error_type  = 'SPACE QUOTA EXCEEDED' AND
             gv_object_type = 'TABLE SPACE' THEN
   
             -- get the username
             OPEN  curs_get_username;
             FETCH curs_get_username INTO gv_username, gv_sid;
             CLOSE curs_get_username;
           
             -- get the current quota
             OPEN  curs_get_ts_quota(  gv_object_name
                                     , gv_username );
             FETCH curs_get_ts_quota INTO gv_old_quota;
             CLOSE curs_get_ts_quota;
   
             -- create an ALTER USER statement and send if off to the fixer job
             -- because if we try it here we will raise
             -- ORA-30511: invalid DDL operation in system triggers
             gv_new_quota := gv_old_quota + 40960;
             gv_sql :=  'ALTER USER '|| gv_username
                     || ' QUOTA '    || gv_new_quota
                     || ' ON '       || gv_object_name;
           
             -- run procedure fix_this in  the package fixer
             fixer.fix_this(gv_sql);
          END IF;
       END IF;
           DBMS_RESUMABLE.abort(gv_sid);
           DBMS_OUTPUT.put_line(chr(10));
           DBMS_OUTPUT.put_line('   Your Space Quota Exceeded !!! Asking DBA for space.');
           DBMS_OUTPUT.put_line(chr(10));
    END;
    /


SYS@ocm> conn hr/hr
Connected.
HR@ocm> alter session enable resumable timeout 3600 name 'xx';
Session altered.

HR@ocm> CREATE TABLE tab_test_suspendx AS SELECT * FROM dba_source;

Your Space Quota Exceeded !!! Asking DBA for space.



CREATE TABLE tab_test_suspendx AS SELECT * FROM dba_source
                                                *
ERROR at line 1:
ORA-01013: user requested cancel of current operation


Supplement:
In addition to the ABORT procedure, the DBMS_RESUMABLE package contains
functions and procedures to get and set timeout values:
GET_SESSION_TIMEOUT
  Returns the timeout value of the suspended session by session ID:
  FUNCTION DBMS_RESUMABLE.GET_SESSION_TIMEOUT (sessionid IN NUMBER)
  RETURN NUMBER;
SET_SESSION_TIMEOUT
  Sets the timeout value of the suspended session by session ID:
  PROCEDURE DBMS_RESUMABLE.SET_SESSION_TIMEOUT (
  sessionid IN NUMBER, TIMEOUT IN NUMBER);
GET_TIMEOUT
  Returns the timeout value of the current session:
  FUNCTION DBMS_RESUMABLE.GET_TIMEOUT RETURN NUMBER;
SET_SESSION_TIMEOUT
  Sets the timeout value of the current session:
  PROCEDURE DBMS_REUSABLE.SET_TIMEOUT (TIMEOUT IN NUMBER);
  New timeout values take effect immediately but do not reset the counter
  t o zero.

 

你可能感兴趣的:(AFTER,suspend,qutoa)