| Security Advisory | 
    
    
      SQL Repair Advisor 
      Must have the Advisor Role - except it may not be true 
       
      DBMS_SQLDIAG only became a potential risk with release of version 12.2 when the I_CREATE_PATCH function was relocated from DBMS_SQLDIAG_INTERNAL to DBMS_SQLDIAG and renamed to CREATE_SQL_PATCH.
      A SQL Patch is a form of DML rewrite where SQL is hinted without requiring that the person or session altering the SQL statement have access to the source code. 
       
      While patching is traditionally thought of as a way to improve DML
      performance there are plenty of real-world examples of patching done poorly bringing an Oracle Database's performance to a very different outcome. One example this author remembers, from a very large mobile phone company, 
      was developers putting the PARALLEL hint into so many SQL statements that they overwhelmed by more than an order of magnitude the number of cpu threads available on the server. 
       
      What makes this especially concerning is Oracle has not thought through the implications of its original decision to grant EXECUTE on this package to PUBLIC. The question that should have been asked, but clearly wasn't, was ".
      Why would anyone with only CREATE SESSION privilege need to be running SQL Diagnostics?" Because, clearly, no one except a DBA or senior developer should be running and interpreting diagnostics.
      The addition of the SQL Patching capability only adds makes the original error in judgment more dangerous. | 
    
    
      |   | 
    
    
      | Recommended Security Rules | 
    
    
       
       NEVER
      
        - Leave Oracle's default GRANT of EXECUTE to PUBLIC in an installed database version 12.2 or above
 
       
       WITH GREAT CARE
      
        - Revoke EXECUTE from PUBLIC
 
       
       CAUTIONS 
      
        - Any GRANT of EXECUTE on this package should be justified with respect to which of the package's 29 objects will be used and why
 
        - Any use of DBMS_SQLDIAG should be carefully monitored to be sure that it complies with the decision to grant the EXECUTE privilege
 
       
       | 
    
    
      |   | 
    
    
      | How Oracle Works | 
    
    
      | SQL Patching | 
      Take a good look at the following 3 SQL statements and their associated explain plans.
      The first statement is not the most efficient that could be written but it isn't too bad. The second increases the cost by 20% from 4 to 5. The third raises the cost from the original 4 to 19 ... a hit on resources that corresponds with 475%.
      The tables in this query had 141 and 999 rows respectively. What would happen if these were really large tables and someone forcd a full table scan?  
       
      EXPLAIN PLAN FOR 
      SELECT DISTINCT s.srvr_id 
      FROM servers s, serv_inst i 
      WHERE s.srvr_id = i.srvr_id; 
       
      SELECT * FROM TABLE(dbms_xplan.display); 
       
      -------------------------------------------------------------------------- 
      | Id  | Operation              | Name         | Rows  | Bytes | Cost (%CPU)| 
      -------------------------------------------------------------------------- 
      |  0 | SELECT STATEMENT       |              |   11 |    88 |    4   (25)| 
      |  1 |  HASH UNIQUE           |              |   11 |    88 |    4   (25)| 
      |  2 |   NESTED LOOPS SEMI    |              |  983 |  7864 |    3    (0)| 
      |  3 |    INDEX FAST FULL SCAN| PK_SERV_INST |  999 |  3996 |    3    (0)| 
      |* 4 |    INDEX UNIQUE SCAN   | PK_SERVERS   |  141 |   564 |    0    (0)| 
      -------------------------------------------------------------------------- 
       
      EXPLAIN PLAN FOR 
      SELECT /*+ USE_HASH(s,i) */ DISTINCT s.srvr_id 
      FROM servers s, serv_inst i 
      WHERE s.srvr_id = i.srvr_id; 
       
      SELECT * FROM TABLE(dbms_xplan.display); 
       
      -------------------------------------------------------------------------- 
      | Id | Operation              | Name         | Rows | Bytes | Cost (%CPU)| 
      -------------------------------------------------------------------------- 
      |  0 | SELECT STATEMENT       |              |   11 |    88 |    5   (20)| 
      |  1 |  HASH UNIQUE           |              |   11 |    88 |    5   (20)| 
      |* 2 |   HASH JOIN SEMI       |              |   11 |    88 |    4    (0)| 
      |  3 |    INDEX FULL SCAN     | PK_SERVERS   |  141 |   564 |    1    (0)| 
      |  4 |    INDEX FAST FULL SCAN| PK_SERV_INST |  999 |  3996 |    3    (0)| 
      -------------------------------------------------------------------------- 
       
      EXPLAIN PLAN FOR 
      SELECT /*+ NO_INDEX(i pk_serv_inst) NO_INDEX(s 
      pk_servers) */ DISTINCT s.srvr_id 
      FROM servers s, serv_inst i 
      WHERE s.srvr_id = i.srvr_id; 
       
      SELECT * FROM TABLE(dbms_xplan.display); 
       
      --------------------------------------------------------------------- 
      | Id | Operation            | Name      | Rows | Bytes | Cost (%CPU)| 
      --------------------------------------------------------------------- 
      |  0 | SELECT STATEMENT     |           |   11 |    88 |    19   (6)| 
      |  1 |  HASH UNIQUE         |           |   11 |    88 |    19   (6)| 
      |* 2 |   HASH JOIN SEMI     |           |   11 |    88 |    18   (0)| 
      |  3 |    TABLE ACCESS FULL | SERVERS   |  141 |   564 |     9   (0)| 
      |  4 |    TABLE ACCESS FULL | SERV_INST |  999 |  3996 |     9   (0)| 
      --------------------------------------------------------------------- 
       
      To create performance degradation above one would need access to the SQL source code, 
      likely embedded in compiled Java or dot net code so the danger of this happening as a deliberate attack is small because a change to the source code would have to take place at the OEM,
      survive testing, and would manifest itself at every customer site. | 
    
    
      | Using a SQL Patch to generate a Denial of Service attack | 
      Now we will perform the exact same attack but at a single customer site. And do it in a way that cannot be observed by any form of network, end-point, or behavioral auditing. 
       
      The first step is to obtain the statement's SQL_ID. 
       
      SQL> SELECT DISTINCT s.srvr_id 
        2  FROM servers s, serv_inst i 
        3  WHERE s.srvr_id = i.srvr_id; 
       
         SRVR_ID 
      ---------- 
              14 
             501 
             504 
             502 
               2 
              12 
             505 
               5 
             506 
               3 
             503 
       
      11 rows selected. 
       
      SELECT sql_id, sql_text 
      FROM v$sqlarea 
      WHERE sql_fulltext LIKE '%SELECT DISTINCT s.srvr_id%'; 
       
      SQL_ID        SQL_TEXT 
      ------------- ------------------------------------------------------- 
      6a0ndq333saxj SELECT DISTINCT s.srvr_id FROM servers s, serv_inst i 
                    WHERE s.srvr_id = i.srvr_id 
       
      9xbc179t3czdp EXPLAIN PLAN FOR SELECT DISTINCT s.srvr_id 
                    FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id 
       
      fs0xz78yarg0k SELECT sql_id, sql_text FROM v$sqlarea WHERE sql_fulltext 
                    LIKE '%SELECT DISTINCT s.srvr_id%' 
       
      The first SQL_ID is the one we want to target. The second is the Explain Plan statement and th third is the statement just executed that returned its own SQL_ID plus the other two. 
       
      DECLARE 
       htxt   CLOB := 'FULL(servers)'; 
       retVal VARCHAR2(60); 
      BEGIN 
        retVal := sys.dbms_sqldiag.create_sql_patch('9babjv8yq8ru3', htxt); 
        dbms_output.put_line(retVal); 
      END; 
      / 
      SYS_SQLPTCH_016c316993fe0003 
       
      PL/SQL procedure successfully completed. 
       
      Now, let's observe the execution plan when we execute the unpatched original SQL statement. Do we get the original statement's HASH UNIQUE and NESTED LOOP SEMI or the FULL TABLE SCAN? 
       
      ALTER SYSTEM FLUSH BUFFER_CACHE; 
      ALTER SYSTEM FLUSH SHARED_POOL; 
       
      SELECT DISTINCT s.srvr_id 
      FROM servers s, serv_inst i 
      WHERE s.srvr_id = i.srvr_id; 
       
      SELECT * FROM TABLE(dbms_xplan.display_cursor); | 
    
    
      |   | 
    
    
      | DBMS_SQLDIAG Package Information | 
    
    
      | AUTHID | 
      CURRENT_USER | 
    
    
      | Constants | 
      
        
          
            
              | Name | 
              Data Type | 
              Value | 
             
            
              |  SQLDiag Advisor Name | 
             
            
              | ADV_SQL_DIAG_NAME | 
              VARCHAR2(18) | 
              'SQL Repair Advisor' | 
             
            
              |  Task Scopes | 
               
            
              | SCOPE_LIMITED | 
              VARCHAR2(7) | 
              'LIMITED' | 
             
            
              | SCOPE_COMPREHENSIVE | 
              VARCHAR2(13) | 
              'COMPREHENSIVE' | 
             
            
              |  Advisor Time Limit | 
             
            
              | TIME_LIMIT_DEFAULT | 
              NUMBER | 
              1800 | 
             
            
              |  Report Types | 
               
            
              | TYPE_HTML | 
              VARCHAR2(4) | 
              'HTML' | 
             
            
              | TYPE_TEXT | 
              VARCHAR2(4) | 
              'TEXT' | 
             
            
              | TYPE_XML | 
              VARCHAR2(3) | 
              'XML' | 
             
            
              |  Report Levels | 
             
            
              | LEVEL_ALL | 
              VARCHAR2(3) | 
              'ALL' | 
             
            
              | LEVEL_BASIC | 
              VARCHAR2(7) | 
              'BASIC' | 
             
            
              | LEVEL_TYPICAL | 
              VARCHAR2(7) | 
              'TYPICAL' | 
             
            
              |  Report Sections | 
             
            
              | SECTION_ALL | 
              VARCHAR2(3) | 
              'ALL' | 
             
            
              | SECTION_ERRORS | 
              VARCHAR2(6) | 
              'ERRORS' | 
             
            
              | SECTION_FINDINGS | 
              VARCHAR2(8) | 
              'FINDINGS' | 
             
            
              | SECTION_INFORMATION | 
              VARCHAR2(11) | 
              'INFORMATION' | 
             
            
              | SECTION_PLANS | 
              VARCHAR2(5) | 
              'PLANS' | 
             
            
              | SECTION_SUMMARY | 
              VARCHAR2(7) | 
              'SUMMARY' | 
             
            
              |  Script Sections | 
             
            
              | REC_TYPE_ALL | 
              VARCHAR2(3) | 
              'ALL' | 
             
            
              | REC_TYPE_INDEXES | 
              VARCHAR2(7) | 
              'INDEXES' | 
             
            
              | REC_TYPE_SQL_PROFILES | 
              VARCHAR2(8) | 
              'PROFILES' | 
             
            
              | REC_TYPE_STATS | 
              VARCHAR2(10) | 
              'STATISTICS' | 
             
            
              |  Capture Sections | 
               
            
              | MODE_REPLACE_OLD_STATS | 
              NUMBER | 
              1 | 
             
            
              | MODE_ACCUMULATE_STATS | 
              NUMBER | 
              2 | 
             
            
              |  Problem Types | 
               
            
              | PROBLEM_TYPE_PERFORMANCE | 
              NUMBER | 
              1 | 
             
            
              | PROBLEM_TYPE_WRONG_RESULTS | 
              NUMBER | 
              2 | 
             
            
              | PROBLEM_TYPE_COMPILATION_ERROR | 
              NUMBER | 
              3 | 
             
            
              | PROBLEM_TYPE_EXECUTION_ERROR | 
              NUMBER | 
              4 | 
             
            
              | PROBLEM_TYPE_ALT_PLAN_GEN | 
              NUMBER | 
              5 | 
             
            
              |  Findings Filters | 
               
            
              | SQLDIAG_FINDINGS_ALL | 
              NUMBER | 
              1 | 
             
            
              | SQLDIAG_FINDINGS_VALIDATION | 
              NUMBER | 
              2 | 
             
            
              | SQLDIAG_FINDINGS_FEATURES | 
              NUMBER | 
              3 | 
             
            
              | SQLDIAG_FINDINGS_FILTER_PLANS | 
              NUMBER | 
              4 | 
             
            
              | SQLDIAG_FINDINGS_CR_DIFF | 
              NUMBER | 
              5 | 
             
            
              | SQLDIAG_FINDINGS_MASK_VARIANT | 
              NUMBER | 
              6 | 
             
            
              | SQLDIAG_FINDINGS_OBJ_FEATURES | 
              NUMBER | 
              7 | 
             
            
              | SQLDIAG_FINDINGS_BASIC_INFO | 
              NUMBER | 
              8 | 
             
            
              |  Mask Mode for Filtering Findings | 
               
            
              | SQLDIAG_MASK_NONE | 
              NUMBER | 
              1 | 
             
            
              | SQLDIAG_MASK_COST | 
              NUMBER | 
              2 | 
             
             | 
    
    
      | Dependencies | 
      
        
          
            
              | ALL_USERS | 
              DBMS_SQLPA | 
              DBMS_XPLAN | 
             
            
              | ANYDATA | 
              DBMS_SQLTCB_INTERNAL | 
              DUAL | 
             
            
              | DBMS_ADVISOR | 
              DBMS_SQLTUNE | 
              PLITBLM | 
             
            
              | DBMS_ASSERT | 
              DBMS_SQLTUNE_INTERNAL | 
              PRVT_ADVISOR | 
             
            
              | DBMS_LOB | 
              DBMS_SQLTUNE_UTIL0 | 
              PRVT_SQLADV_INFRA | 
             
            
              | DBMS_MANAGEMENT_PACKS | 
              DBMS_SQLTUNE_UTIL1 | 
              PRVT_SQLPROF_INFRA | 
             
            
              | DBMS_PDB | 
              DBMS_SQLTUNE_UTIL2 | 
              SQLSET_ROW | 
             
            
              | DBMS_PDB_CHECK_LOCKDOWN | 
              DBMS_STANDARD | 
              SQL_BINDS | 
             
            
              | DBMS_SMB | 
              DBMS_STATS | 
              V$SQL | 
             
            
              | DBMS_SMB_INTERNAL | 
              DBMS_STATS_INTERNAL | 
              XMLSEQUENCE | 
             
            
              | DBMS_SPM | 
              DBMS_STATS_INTERNAL_AGG | 
              XMLTYPE | 
             
            
              | DBMS_SQLDIAG_INTERNAL | 
              DBMS_SYS_ERROR | 
              XQSEQUENCE | 
             
             | 
    
    
      | Documented | 
      Yes | 
    
    
      | First Available | 
      11.1.0.6 | 
    
    
      | Security Model | 
      Owned by SYS with EXECUTE granted to PUBLIC (a 
      grant that is a clear violation of the Principle of Least Privilege). 
       
      Use of this package requires the ADVISOR system privilege. 
       
      ALTER ANY SQL PATCH, CREATE ANY SQL PATCH, and/or DROP ANY SQL PATCH must be granted to utilize the corresponding functionality. | 
    
    
      | Source | 
      {ORACLE_HOME}/rdbms/admin/dbmsdiag.sql | 
    
    
      | Subprograms | 
      
         | 
    
    
      |   | 
    
    
      | ACCEPT_SQL_PATCH | 
    
    
      This procedure accepts a SQL patch as recommended by the specified SQL tuning task 
       
      Overload 1 | 
      dbms_sqldiag.accept_sql_patch( 
      task_name   IN VARCHAR2, 
      object_id   IN NUMBER   := NULL, 
      name        IN VARCHAR2 := NULL, 
      description IN VARCHAR2 := NULL, 
      category    IN VARCHAR2 := NULL, 
      task_owner  IN VARCHAR2 := NULL, 
      replace     IN BOOLEAN  := FALSE, 
      force_match  IN BOOLEAN  := FALSE) 
      RETURN VARCHAR2; | 
    
    
      | TBD | 
    
    
      | Overload 2 | 
      dbms_sqldiag.accept_sql_patch( 
      task_name   IN VARCHAR2, 
      object_id   IN NUMBER   := NULL, 
      name        IN VARCHAR2 := NULL, 
      description  IN VARCHAR2 := NULL, 
      category    IN VARCHAR2 := NULL, 
      task_owner  IN VARCHAR2 := NULL, 
      replace     IN BOOLEAN  := FALSE, 
      force_match  IN BOOLEAN  := FALSE); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | ALTER_SQL_PATCH | 
    
    
      | This procedure alters specific attributes of an existing SQL patch object | 
      dbms_sqldiag.alter_sql_patch( 
      name           IN VARCHAR2, 
      attribute_name IN VARCHAR2, 
      value          IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | CANCEL_DIAGNOSIS_TASK | 
    
    
      | Cancels a diagnostic task | 
      dbms_sqldiag.cancel_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      exec dbms_sqldiag.cancel_diagnosis_task('ERROR_TASK'); | 
    
    
      |   | 
    
    
      | CREATE_DIAGNOSIS_TASK | 
    
    
      Creates a diagnostic task in order to diagnose a single SQL statement 
       
      Overload 1 | 
      dbms_sqldiag.create_diagnosis_task( 
      sql_text     IN CLOB, 
      bind_list    IN sql_binds := NULL, 
      user_name    IN VARCHAR2  := NULL, 
      scope        IN VARCHAR2  := SCOPE_COMPREHENSIVE, 
      time_limit   IN NUMBER    := TIME_LIMIT_DEFAULT, 
      task_name    IN VARCHAR2  := NULL, 
      description  IN VARCHAR2  := NULL, 
      problem_type IN NUMBER    := PROBLEM_TYPE_PERFORMANCE) 
      RETURN VARCHAR2; | 
    
    
      | See Demo below | 
    
    
      | Overload 2 | 
      dbms_sqldiag.create_diagnosis_task( 
      sql_id          IN VARCHAR2, 
      plan_hash_value IN NUMBER   := NULL, 
      scope           IN VARCHAR2 := SCOPE_COMPREHENSIVE, 
      time_limit      IN NUMBER   := TIME_LIMIT_DEFAULT, 
      task_name       IN VARCHAR2 := NULL, 
      description     IN VARCHAR2 := NULL, 
      problem_type    IN NUMBER   := PROBLEM_TYPE_PERFORMANCE) 
      RETURN VARCHAR2; | 
    
    
      | TBD | 
    
    
      | Overload 3 | 
      dbms_sqldiag.create_diagnosis_task( 
      sqlset_name       IN VARCHAR2, 
      basic_filter      IN VARCHAR2 := NULL, 
      object_filter     IN VARCHAR2 := NULL, 
      rank1             IN VARCHAR2 := NULL, 
      rank2             IN VARCHAR2 := NULL, 
      rank3             IN VARCHAR2 := NULL, 
      result_percentage IN NUMBER   := NULL, 
      result_limit      IN NUMBER   := NULL, 
      scope             IN VARCHAR2 :=  SCOPE_COMPREHENSIVE, 
      time_limit        IN NUMBER   := TIME_LIMIT_DEFAULT, 
      task_name         IN VARCHAR2 := NULL, 
      description       IN VARCHAR2 := NULL, 
      plan_filter       IN VARCHAR2 := 'MAX_ELAPSED_TIME', 
      sqlset_owner      IN VARCHAR2 := NULL, 
      problem_type      IN NUMBER   := PROBLEM_TYPE_PERFORMANCE) 
      RETURN VARCHAR2; | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | CREATE_SQL_PATCH | 
    
    
      Creates a SQL patch based on a set of user specified hints for specific statements identified by the SQL text.
      A SQL patch is usually created automatically by the SQL Repair Advisor to prevent any errors during the compilation or execution of a SQL statement. This procedure provides a way to manually create a SQL patch based on a set of hints that resolves the issue. 
       
      Overload 1 | 
      dbms_sqldiag.create_sql_patch( 
      sql_text   IN CLOB, 
      hint_text  IN CLOB, 
      name       IN VARCHAR2 := NULL, 
      decription IN VARCHAR2 := NULL, 
      category   IN VARCHAR2 := NULL, 
      validate   IN BOOLEAN  := TRUE) 
      RETURN VARCHAR2; | 
    
    
      SQL> variable srvrid number; 
      SQL> exec :srvrid := 12 
       
      PL/SQL procedure successfully completed. 
       
      SELECT /* CREATE_PATCH1 */ COUNT(*), MAX(siid) 
      FROM uwclass.serv_inst 
      WHERE srvr_id = :srvrid; 
       
        COUNT(*) MAX(SIID) 
      ---------- ---------- 
              22   3714862 
       
      SELECT sql_id, sql_text 
      FROM v$sqlarea 
      WHERE sql_fulltext LIKE '%CREATE_PATCH%'; 
       
      SQL_ID        SQL_TEXT 
      ------------- ----------------------------------------------- 
      0taz20gu81tvd
      SELECT /* CREATE_PATCH1 */ COUNT(*), MAX(siid)  
                    FROM uwclass.serv_inst WHERE srvr_id = :srvrid 
       
      SELECT is_bind_aware 
      FROM v$sql 
      WHERE sql_id = '0taz20gu81tvd'; 
       
      I 
      - 
      N 
       
      DECLARE 
        stxt CLOB := 'SELECT /* CREATE_PATCH2 */ COUNT(*), MAX(siid) ' || 
                     'FROM uwclass.serv_inst WHERE srvr_id = :srvrid'; 
        htxt CLOB := 'BIND_AWARE'; 
        retVal VARCHAR2(60); 
      BEGIN 
        retVal := sys.dbms_sqldiag.create_sql_patch(stxt, htxt); 
        dbms_output.put_line(retVal); 
      END; 
      / 
      SYS_SQLPTCH_0161abef311f0000 
       
      PL/SQL procedure successfully completed. | 
    
    
      Creates a SQL patch based on a set of user specified hints for specific statements identified by the SQL id.
      A SQL patch is usually created automatically by the SQL Repair Advisor to prevent any errors during the compilation or execution of a SQL statement. This procedure provides a way to manually create a SQL patch based on a set of hints that resolves the issue. 
       
      Overload 2 | 
      dbms_sqldiag.create_sql_patch( 
      sql_id     IN VARCHAR2, 
      hint_text  IN CLOB, 
      name       IN VARCHAR2 := NULL, 
      decription IN VARCHAR2 := NULL, 
      category   IN VARCHAR2 := NULL, 
      validate   IN BOOLEAN  := TRUE) 
      RETURN VARCHAR2; | 
    
    
      DECLARE 
       htxt   CLOB := 'NO_INDEX(i pk_serv_inst) 
      NO_INDEX(s pk_servers)'; 
       retVal VARCHAR2(60); 
      BEGIN 
        retVal := sys.dbms_sqldiag.create_sql_patch('6a0ndq333saxj', htxt); 
        dbms_output.put_line(retVal); 
      END; 
      / | 
    
    
      |   | 
    
    
      | CREATE_STGTAB_SQLPATCH | 
    
    
      | Creates the staging table used for transporting SQL patches from one system to another | 
      dbms_sqldiag.create_stgtab_sqlpatch( 
      table_name      IN VARCHAR2, 
      schema_name     IN VARCHAR2 := NULL, 
      tablespace_name IN VARCHAR2 := NULL); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | DROP_DIAGNOSIS_TASK | 
    
    
      | Drops a diagnostic task | 
      dbms_sqldiag.drop_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | DROP_SQL_PATCH | 
    
    
      | This procedure drops the named SQL patch from the database | 
      dbms_sqldiag.drop_sql_patch( 
      name   IN VARCHAR2, 
      ignore IN BOOLEAN  := FALSE); | 
    
    
      SQL> exec dbms_sqldiag.drop_sql_patch('SYS_SQLPTCH_016c315f3f6a0000'); 
       
      PL/SQL procedure successfully completed. | 
    
    
      |   | 
    
    
      | DUMP_TRACE | 
    
    
      | Dump Optimizer Trace | 
      dbms_sqldiag.dump_trace( 
      p_sql_id       IN VARCHAR2, 
      p_child_number IN NUMBER   DEFAULT 0,  
      p_component    IN VARCHAR2 DEFAULT 'Optimizer', -- alt. value 'Compiler' 
      p_file_id      IN VARCHAR2 DEFAULT NULL); | 
    
    
      SELECT /* DUMP_TRACE */ DISTINCT s.srvr_id 
      FROM servers s, serv_inst i 
      WHERE s.srvr_id = i.srvr_id; 
       
      SELECT sql_id, child_number 
      FROM v$sql 
      WHERE sql_text LIKE '%DUMP_TRACE%'; 
       
      exec dbms_sqldiag.dump_trace('gk3bbazk1jj3h', 0); 
       
      -- the file was dumped as: 
      /app/oracle/product/diag/rdbms/orabeta/orabeta/trace/orabeta_dbrm_18833.trc | 
    
    
      |   | 
    
    
      | EXECUTE_DIAGNOSIS_TASK | 
    
    
      | Executes a diagnostic task | 
      dbms_sqldiag.execute_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      See Demo below | 
    
    
      |   | 
    
    
      | EXPLAIN_SQL_TESTCASE | 
    
    
      | Explains a SQL test case. The docs say the input should be an XML document but give no indication of where it is supposed to come from. | 
      dbms_sqldiag.explain_sql_testcase(sqlTestCase IN CLOB) 
      RETURN CLOB; | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | EXPORT_SQL_TESTCASE | 
    
    
      Export a SQL test case to a directory. This variant of the API has to be provided with the SQL information 
       
      Overload 1 | 
      dbms_sqldiag.export_sql_testcase( 
      directory             IN            VARCHAR2, 
      sql_text              IN            CLOB, 
      user_name             IN            VARCHAR2  := NULL, 
      bind_list             IN            sql_binds := NULL, 
      exportEnvironment     IN            BOOLEAN   := TRUE, 
      exportMetadata        IN            BOOLEAN   := TRUE, 
      exportData            IN            BOOLEAN   := FALSE, 
      samplingPercent       IN            NUMBER    := 100, 
      ctrlOptions           IN            VARCHAR2  := NULL, 
      timeLimit             IN            NUMBER    := 0, 
      testcase_name         IN            VARCHAR2  := NULL, 
      testcase              IN OUT NOCOPY CLOB, 
      preserveSchemaMapping IN            BOOLEAN   := FALSE, 
      version               IN            VARCHAR2  := 'COMPATIBLE'); | 
    
    
      | TBD | 
    
    
      Export a SQL test case to a directory. This API extract the SQL information from an incident file. 
       
      Overload 2 | 
      dbms_sqldiag.export_sql_testcase( 
      directory              IN            VARCHAR2, 
      incident_id            IN            VARCHAR2, 
      exportEnvironment       IN            BOOLEAN  := TRUE, 
      exportMetadata         IN            BOOLEAN  := TRUE, 
      exportData             IN            BOOLEAN  := FALSE, 
      samplingPercent        IN            NUMBER   := 100, 
      ctrlOptions            IN            VARCHAR2 :=  NULL, 
      timeLimit              IN            NUMBER   :=  0, 
      testcase_name          IN            VARCHAR2 :=  NULL, 
      testcase               IN OUT NOCOPY CLOB, 
      preserveSchemaMapping  IN            BOOLEAN  := FALSE, 
      version                IN            VARCHAR2 := 'COMPATIBLE'); | 
    
    
      | TBD | 
    
    
      Export a SQL test case to a directory. This API allow the SQL Testcase to be generated from a cursor present in the cursor cache. Use v$sql to get the SQL identifier and the SQL hash value. 
       
      Overload 3 | 
      dbms_sqldiag.export_sql_testcase( 
      directory             IN            VARCHAR2, 
      sql_id                IN            VARCHAR2, 
      plan_hash_value       IN            NUMBER   := NULL, 
      exportEnvironment     IN            BOOLEAN  := TRUE, 
      exportMetadata        IN            BOOLEAN  := TRUE, 
      exportData            IN            BOOLEAN  := FALSE, 
      samplingPercent       IN            NUMBER   := 100, 
      ctrlOptions           IN            VARCHAR2 :=  NULL, 
      timeLimit             IN            NUMBER   := 0, 
      testcase_name         IN            VARCHAR2  :=  NULL, 
      testcase              IN OUT NOCOPY CLOB, 
      preserveSchemaMapping IN            BOOLEAN  := FALSE, 
      version               IN            VARCHAR2 := 'COMPATIBLE'); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | EXPORT_SQL_TESTCASE_DIR_BY_INC | 
    
    
      | Export a test case to a directory | 
      dbms_sqldiag.export_sql_testcase_dir_by_inc( 
      incident_id           IN NUMBER, 
      directory             IN VARCHAR2, 
      samplingPercent       IN VARCHAR2 := '0', 
      exportEnvironment     IN BOOLEAN  := TRUE, 
      exportMetadata        IN BOOLEAN  := TRUE, 
      exportPkgbody         IN BOOLEAN  := FALSE, 
      preserveSchemaMapping IN BOOLEAN  := FALSE, 
      version               IN VARCHAR2 := 'COMPATIBLE') 
      RETURN BOOLEAN; | 
    
    
      -- go to $ORACLE_BASE/diag/rdbms/orabeta/orabeta/incident and view subdirectory incdir_3809 
       
      set serveroutput on 
       
      DECLARE 
       v_inc NUMBER := 3809; 
       v_dir VARCHAR2(30) := 'CTEMP'; 
      BEGIN 
        IF sys.dbms_sqldiag.export_sql_testcase_dir_by_inc(v_inc, v_dir) THEN 
          dbms_output.put_line('Package Created'); 
        ELSE 
          dbms_output.put_line('Package Creation Failure'); 
        END IF; 
      END; 
      / 
       
      SQL> DECLARE 
        2   v_inc NUMBER := 3809; 
        3   v_dir VARCHAR2(30) := 'CTEMP'; 
        4  BEGIN 
        5    IF sys.dbms_sqldiag.export_sql_testcase_dir_by_inc(v_inc, v_dir) THEN 
        6      dbms_output.put_line('Package Created'); 
        7    ELSE 
        8      dbms_output.put_line('Package Creation Failure'); 
        9    END IF; 
       10  END; 
       11  / 
      DECLARE 
      * 
      ERROR at line 1: 
      ORA-39087: directory name TCBORA$PIPE$008C07960001 is invalid 
      ORA-06512: at "SYS.DBMS_SYS_ERROR", line 86 
      ORA-06512: at "SYS.DBMS_SQLDIAG", line 92 
      ORA-06512: at "SYS.DBMS_SQLDIAG", line 312 
      ORA-06512: at line 5 
       
      Note these are not the same as ADRCI indicent numbers 
      The docs are terrible on this | 
    
    
      |   | 
    
    
      | EXPORT_SQL_TESTCASE_DIR_BY_TXT | 
    
    
      | Generates a SQL Test Case corresponding to the SQL passed as an argument | 
      dbms_sqldiag.export_sql_testcase_dir_by_txt( 
      incident_id           IN NUMBER, 
      directory             IN VARCHAR2, 
      sql_text              IN CLOB, 
      user_name             IN VARCHAR2 := NULL, 
      samplingPercent       IN VARCHAR2 := '0', 
      exportEnvironment     IN BOOLEAN  := TRUE, 
      exportMetadata        IN BOOLEAN  := TRUE, 
      exportPkgbody         IN BOOLEAN  := FALSE, 
      preserveSchemaMapping IN BOOLEAN  := FALSE, 
      version               IN VARCHAR2 := 'COMPATIBLE') 
      RETURN BOOLEAN; | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | GETSQL | 
    
    
      | Load a sql_setrow from the trace file associated with an incident ID | 
      dbms_sqldiag.getsql(incident_id IN VARCHAR2) RETURN SQLSET_ROW; | 
    
    
      SELECT num_incident, check_name, name, timeout 
      FROM gv$hm_run; 
       
      desc sqlset_row 
       
      set serveroutput on 
       
      DECLARE 
       z   VARCHAR2(100); 
       ssr sqlset_row; 
      BEGIN 
        ssr := dbms_sqldiag.getsql(1); 
        dbms_output.put_line(z); 
      END; 
      / | 
    
    
      |   | 
    
    
      | GET_FIX_CONTROL | 
    
    
      | Returns the value of fix control for a given bug number | 
      dbms_sqldiag.get_fix_control(bug_number IN NUMBER) RETURN NUMBER; | 
    
    
      SELECT dbms_sqldiag.get_fix_control(13) 
      FROM dual; | 
    
    
      |   | 
    
    
      | IMPORT_SQL_TESTCASE | 
    
    
      Import a SQL Test case into a schema from a directory and a file name 
       
      Overload 1 | 
      dbms_sqldiag.import_sql_testcase( 
      directory             IN VARCHAR2, 
      sqlTestCase           IN CLOB, 
      importEnvironment     IN BOOLEAN  := TRUE, 
      importMetadata        IN BOOLEAN  := TRUE, 
      importData            IN BOOLEAN  := FALSE, 
      importDiagnosis       IN BOOLEAN  := TRUE, 
      ignoreStorage         IN BOOLEAN  := TRUE, 
      ctrlOptions           IN VARCHAR2 := NULL, 
      preserveSchemaMapping IN BOOLEAN  := FALSE); | 
    
    
      | TBD | 
    
    
      Initialize a sql_setrow from an incident ID.
      Given a valid incident ID this function parses the trace file and extract as much information as possible about the SQL that causes the generation of this incident (SQL text, user name, binds, etc...). 
       
      Overload 2 | 
      dbms_sqldiag.import_sql_testcase( 
      directory             IN VARCHAR2, 
      filename              IN VARCHAR2, 
      importEnvironment     IN BOOLEAN  := TRUE, 
      importMetadata        IN BOOLEAN  := TRUE, 
      importData            IN BOOLEAN  := FALSE, 
      importDiagnosis       IN BOOLEAN  := TRUE, 
      ignoreStorage         IN BOOLEAN  := TRUE, 
      ctrlOptions           IN VARCHAR2 := NULL, 
      preserveSchemaMapping IN BOOLEAN  := FALSE); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | INCIDENTID_2_SQL | 
    
    
      | Initializes a sql_setrow from an incident ID | 
      dbms_sqldiag.incidentid_2_sql( 
      incident_id  IN  VARCHAR2, 
      sql_stmt     OUT SQLSET_ROW, 
      problem_type OUT NUMBER, 
      err_code     OUT BINARY_INTEGER, 
      err_mesg     OUT VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | INTERRUPT_DIAGNOSIS_TASK | 
    
    
      | Interrupts a diagnostic task | 
      dbms_sqldiag.interrupt_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | LOAD_SQLSET_FROM_TCB | 
    
    
      | Loads a SQLSET from Test Case Builder (TCB) file | 
      dbms_sqldiag.interrupt_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | PACK_STGTAB_SQLPATCH | 
    
    
      | SQL patches into the staging table created by the CREATE_STGTAB_SQLPATCH Procedure | 
      dbms_sqldiag.pack_stgtab_sqlpatch( 
      patch_name           IN VARCHAR2 := '%', 
      patch_category       IN VARCHAR2 := 'DEFAULT', 
      staging_table_name   IN VARCHAR2, 
      staging_schema_owner IN VARCHAR2 := NULL); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | REPLAY_SQL_TESTCASE | 
    
    
      Replays a SQL test case 
       
      Overload 1 | 
      dbms_sqldiag.explain_sql_testcase( 
      directory   IN VARCHAR2, 
      sqlTestCase IN CLOB, 
      ctrlOptions IN VARCHAR2 := NULL, 
      format      IN VARCHAR2 := 'TEXT') 
      RETURN CLOB; | 
    
    
      | TBD | 
    
    
      | Overload 2 | 
      dbms_sqldiag.explain_sql_testcase( 
      directory   IN VARCHAR2, 
      filename    IN VARCHAR2, 
      ctrlOptions IN VARCHAR2 := NULL, 
      format      IN VARCHAR2 := 'TEXT') 
      RETURN CLOB; | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | REPORT_DIAGNOSIS_TASK | 
    
    
      | Reports on a diagnostic task | 
      dbms_sqldiag.report_diagnosis_task( 
      task_name    IN VARCHAR2, 
      type         IN VARCHAR2 := TYPE_TEXT, 
      level        IN VARCHAR2 := LEVEL_ALL, 
      section      IN VARCHAR2 := SECTION_ALL, 
      object_id    IN NUMBER   := NULL, 
      result_limit IN NUMBER   := NULL, 
      owner_name   IN VARCHAR2 := NULL) 
      RETURN CLOB; | 
    
    
      | See Demo Below | 
    
    
      |   | 
    
    
      | RESET_DIAGNOSIS_TASK | 
    
    
      | Resets a diagnostic task | 
      dbms_sqldiag.reset_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | RESUME_DIAGNOSIS_TASK | 
    
    
      | Resume a diagnostic task | 
      dbms_sqldiag.resume_diagnosis_task(task_name IN VARCHAR2); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | SET_DIAGNOSIS_TASK_PARAMETER | 
    
    
      | Sets a diagnosis task parameter | 
      dbms_sqldiag.set_diagnosis_task_parameter( 
      task_name IN VARCHAR2, 
      parameter IN VARCHAR2, 
      value     IN NUMBER); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | SET_TCB_TRACING | 
    
    
      | Enables/disables TCB tracing (for Oracle Support/Development use only) | 
      dbms_sqldiag.set_tcb_tracing(status IN BOOLEAN := TRUE); | 
    
    
      exec dbms_sqldiag.set_tcb_tracing(FALSE); | 
    
    
      |   | 
    
    
      | SQL_DIAGNOSE_AND_REPAIR | 
    
    
      Diagnose a given SQL statement for the given problem type. 
      -- It creates an incident, populate incident metadata with 
      -- required information like, sqlid, sql text, compilation env etc, 
      -- creates a diagnostic task, executes it and accepts SQL PATCH  
      -- recommendation for a given SQL statement. 
       
      Overload 1 | 
      dbms_sqldiag.sql_diagnose_and_repair( 
      sql_id           IN VARCHAR2, 
      plan_hash_value  IN NUMBER   := NULL, 
      scope            IN VARCHAR2 := SCOPE_COMPREHENSIVE, 
      time_limit       IN NUMBER   := TIME_LIMIT_DEFAULT, 
      problem_type     IN NUMBER   := PROBLEM_TYPE_PERFORMANCE, 
      auto_apply_patch IN VARCHAR2 := 'YES') 
      RETURN NUMBER; | 
    
    
      | TBD | 
    
    
      | Overload 2 | 
      dbms_sqldiag.sql_diagnose_and_repair( 
      sql_id           IN CLOB, 
      bind_list        IN sql_binds := NULL, 
      scope            IN VARCHAR2  := SCOPE_COMPREHENSIVE, 
      time_limit       IN NUMBER    := TIME_LIMIT_DEFAULT, 
      problem_type     IN NUMBER    := PROBLEM_TYPE_PERFORMANCE, 
      auto_apply_patch IN VARCHAR2  := 'YES') 
      RETURN NUMBER; | 
    
    
      | TBD | 
    
    
      | Overload 3 | 
      dbms_sqldiag.sql_diagnose_and_repair( 
      incident_id      IN NUMBER, 
      scope            IN VARCHAR2  := SCOPE_COMPREHENSIVE, 
      time_limit       IN NUMBER    := TIME_LIMIT_DEFAULT, 
      problem_type     IN NUMBER    := PROBLEM_TYPE_PERFORMANCE, 
      auto_apply_patch IN VARCHAR2  := 'YES') 
      RETURN NUMBER; | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | UNPACK_STGTAB_SQLPATCH | 
    
    
      | Unpacks from the staging table populated by a call to PACK_STGTAB_SQLPATCH, using the patch data stored in the staging table to create patches on this system | 
      dbms_sqldiag.unpack_stgtab_sqlpatch( 
      patch_name           IN VARCHAR2 := '%', 
      patch_category       IN VARCHAR2 := '%', 
      replace              IN BOOLEAN, 
      staging_table_name   IN VARCHAR2, 
      staging_schema_owner IN VARCHAR2 := NULL); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | Demo | 
    
    
      | DBMS_SQLDIAG Demo | 
      conn uwclass/uwclass@pdbdev 
       
      CREATE TABLE t ( 
      a  VARCHAR2(3), 
      b  VARCHAR2(3), 
      c  VARCHAR2(3), 
      d  VARCHAR2(3)); 
       
      INSERT INTO t VALUES ('a', 'b', 'c', 'd'); 
      INSERT INTO t VALUES ('u', 'v', 'w', 'd'); 
      INSERT INTO t VALUES ('a', 'b', 'c', 'z'); 
      INSERT INTO t VALUES ('w', 'x', 'y', 'd'); 
       
      SELECT * FROM t; 
       
      -- a critical error occurs 
      DELETE FROM uwclass.t t1 
      WHERE t1.a = 'a' 
      AND ROWID <> ( 
        SELECT MAX(ROWID) 
        FROM uwclass.t t2 
        WHERE t1.a= t2.a 
        AND t1.b = t2.b AND t1.d=t2.d); 
       
      SELECT task_id, task_name, created, advisor_name, status 
      FROM dba_advisor_tasks 
      ORDER BY 1; 
       
      set long 1000000 
      set serveroutput on 
       
      DECLARE 
       rep_out CLOB; 
       t_id    VARCHAR2(50); 
      BEGIN 
        -- create a diagnosis task 
        t_id := dbms_sqldiag.create_diagnosis_task( 
        sql_text => 'DELETE FROM uwclass.t t1 WHERE t1.a = ''a'' AND ROWID <> 
        (SELECT   MAX(ROWID) FROM uwclass.t t2 WHERE t1.a= t2.a AND t1.b = t2.b AND 
        t1.d=t2.d)', task_name => 'error_task', problem_type => 
        DBMS_SQLDIAG.PROBLEM_TYPE_COMPILATION_ERROR); 
       
        -- run the diagnosis task 
        dbms_sqldiag.execute_diagnosis_task(t_id); 
       
        -- output the report 
        rep_out := dbms_sqldiag.report_diagnosis_task(t_id, 
        dbms_sqldiag.type_text); 
       
        dbms_output.put_line('Report : ' || rep_out); 
      END; 
      / 
       
      SELECT task_id, task_name, created, advisor_name, status 
      FROM dba_advisor_tasks 
      ORDER BY 1; 
       
      -- apply the recommended patch 
      exec dbms_sqldiag.accept_sql_patch(task_name => 'error_task', task_owner => 'SYS', replace => TRUE); 
       
      -- test the patch 
      DELETE FROM t t1 
      WHERE t1.a = 'a' 
      AND ROWID <> (SELECT MAX(rowid) FROM t t2 WHERE t1.a= t2.a AND t1.b = t2.b AND t1.d=t2.d); 
       
      SELECT task_id, task_name, created, advisor_name, status 
      FROM dba_advisor_tasks 
      ORDER BY 1; 
       
      dbms_sqltune.drop_tuning_task(task_name => 'error_task'); |