IS IF display_TF AND (current_module IS NOT NULL OR current_client_info IS NOT NULL) THEN END set_stats; 7.3.5 Using the register_app Package The following SQL*Plus script demonstrates how the register_app package can be used to register each step of a multistep batch process. The script displays the resource utilization statistics that have been collected for each step. While the script is executing, DBAs can monitor which step is currently running by querying V$SESSION for the session executing the script. /* Filename on companion disk: regtest.sql */* rem ====================================================== rem REGTEST.SQL rem rem SQL*Plus script to demonstrate the use of package rem REGISTER_APP for tracking performance statistics rem rem ====================================================== set serveroutput on size 100000 set feedback off rem ====================================================== rem register module first with display OFF to rem initialize stats, then set display ON rem ====================================================== execute register_app.set_display_TF(FALSE); execute register_app.module('REGTEST.SQL'); execute register_app.set_display_TF(TRUE); set feedback on rem ====================================================== rem create a table my_dictionary copied from dictionary rem ====================================================== execute register_app.action('CREATE'); CREATE TABLE my_dictionary (id, table_name, comments) TABLESPACE user_data2 AS SELECT rownum,A.* FROM dictionary A; rem ====================================================== rem update one third of my_dictionary rows rem ====================================================== execute register_app.action('UPDATE'); UPDATE my_dictionary SET comments = RPAD(comments,2000,'*') WHERE MOD(id,3) = 0; rem ====================================================== rem delete one third of my_dictionary rows rem ====================================================== execute register_app.action('DELETE'); DELETE FROM my_dictionary WHERE MOD(id,3) = 1; [Appendix A] What's on the Companion Disk? 7.3.5 Using the register_app Package 381 rem ====================================================== rem drop table my_dictionary rem ====================================================== execute register_app.action('DROP'); DROP TABLE my_dictionary; rem ====================================================== rem unregister and display previous step stats rem ====================================================== execute register_app.module(null,null); Here is sample output generated by the script: SQL> @regtest Module: REGTEST.SQL Action: BEGIN Client Info: Stats: elapsed secs: .15, physical reads: 0, logical reads: 0 PL/SQL procedure successfully completed. Table created. Module: REGTEST.SQL Action: CREATE Client Info: Stats: elapsed secs: 15.93, physical reads: 137, logical reads: 8407 PL/SQL procedure successfully completed. 92 rows updated. Module: REGTEST.SQL Action: UPDATE Client Info: Stats: elapsed secs: 9.32, physical reads: 8, logical reads: 2075 PL/SQL procedure successfully completed. 93 rows deleted. Module: REGTEST.SQL Action: DELETE Client Info: Stats: elapsed secs: .6, physical reads: 0, logical reads: 296 PL/SQL procedure successfully completed. Table dropped. Module: REGTEST.SQL Action: DROP Client Info: Stats: elapsed secs: 5.36, physical reads: 35, logical reads: 356 PL/SQL procedure successfully completed. 7.3.6 Covering DBMS_APPLICATION_INFO Oracle suggests in the DBMS_APPLICATION_INFO package documentation that DBAs may want to develop a cover package called DBMS_APPLICATION_INFO in a schema other than SYS. By redirecting the public synonym DBMS_APPLICATION_INFO to point at this version of the package, any programs [Appendix A] What's on the Companion Disk? 7.3.6 Covering DBMS_APPLICATION_INFO 382 referencing DBMS_APPLICATION_INFO programs will use the new package. Any functional extensions to DBMS_APPLICATION_INFO in the cover package will be immediately picked up by programs using DBMS_APPLICATION_INFO. In this way, resource tracking like that demonstrated by the register_app package can be implemented globally for programs using DBMS_APPLICATION_INFO. Instead of directly covering DBMS_APPLICATION_INFO with a package of the same name, I chose to create the register_app package. One reason for this: I prefer the shorter and more meaningful name register_app. New applications can call register_app directly and avoid the painfully long DBMS_APPLICATION_INFO package name. Another reason was that I wanted to extend the functionality of DBMS_APPLICATION_INFO with new programs, and thus the new package would not look identical to DBMS_APPLICATION_INFO. When covering an Oracle built−in package, it is good practice to create a package with an identical specification (or API) to that of the built−in. We can actually cover DBMS_APPLICATION_INFO with a package that calls the register_app programs. In this way, the functionality of register_app is extended to programs that reference DBMS_APPLICATION_INFO directly, and we still have our new package to use for new programs. The following code shows how DBMS_APPLICATION_INFO.SET_MODULE can be covered in this way: CREATE OR REPLACE PACKAGE BODY DBMS_APPLICATION_INFO IS PROCEDURE set_module (module_name IN VARCHAR2 ,action_name IN VARCHAR2) IS register_app.module(module_name, action_name); END set_module; Notice that the SET_MODULE cover procedure is identical in signature to the program of the same name in the SYS version of the DBMS_APPLICATION_INFO package. Q: Why must the cover package for DBMS_APPLICATION_INFO match all program signatures identically, including parameter names? A: The program signatures in the cover package to DBMS_APPLICATION_INFO must match those in the SYS version of the package because existing calls to DBMS_APPLICATION_INFO could otherwise be compromised. It is necessary to match not only the number of parameters and their datatypes and modes (IN or OUT) but also the parameter names. The parameter names must match in order to preserve functionality in existing programs calling DBMS_APPLICATION_INFO using named notation. The following fragment illustrates code that will not work if the cover package does not preserve parameter names in the signature for the SET_MODULE procedure: DECLARE module_var VARCHAR2(64) := 'Program 1'; action_var VARCHAR2(64) := 'Transaction A'; BEGIN DBMS_APPLICATION_INFO.SET_MODULE (module_name=>module_var ,action=>action_var); END; Q: What necessary precaution was taken in the register_app package to ensure that it could be used as part of a cover package for DBMS_APPLICATION_INFO? A: All calls to DBMS_APPLICATION_INFO in the register_app package are fully qualified with the schema name (SYS). This way, when the public synonym DBMS_APPLICATION_INFO is redirected to point at the cover package, an infinite loop is avoided and the SYS version of the package is ultimately called. [Appendix A] What's on the Companion Disk? 7.3.6 Covering DBMS_APPLICATION_INFO 383 Exercise for the reader: Create the full cover package for DBMS_APPLICATION_INFO using the register_app package. 7.3.7 Monitoring Application SQL Resource Consumption When applications make use of DBMS_APPLICATION_INFO to register themselves, DBAs can monitor application usage and resource consumption through the V$SESSION and V$SQLAREA virtual tables. The following is a simple report summarizing SQL resource consumption data by application module and action. Such reports can serve a number of useful purposes, including the following: • Identifying tuning opportunities • Quantifying utilization levels by application component • Implementing chargeback schemes /* Filename on companion disk: sqlarea.sql */ rem ====================================================== rem SQLAREA.SQL rem Simple report from V$SQLAREA on SQL resource rem utilization by module and action rem ====================================================== col module format a15 col action format a15 SELECT module ,action ,SUM(buffer_gets) buffer_gets ,SUM(rows_processed) rows_processed ,SUM(disk_reads) disk_reads FROM sys.v_$sqlarea WHERE module IS NOT NULL AND action IS NOT NULL GROUP BY module, action; The following output was generated by the script after regtest.sql had been executed several times: SQL> @sqlarea MODULE ACTION BUFFER_GETS ROWS_PROCESSED DISK_READS −−−−−−−−−−−−− −−−−−−−−−−−−−−− −−−−−−−−−−− −−−−−−−−−−−−−− −−−−−−−−−− REGTEST.SQL BEGIN 0 7 0 REGTEST.SQL CREATE 0 7 0 REGTEST.SQL DELETE 1014 313 0 REGTEST.SQL DROP 0 7 0 REGTEST.SQL UPDATE 6721 308 33 5 rows selected. 7.3.8 Session Monitoring and Three−Tier Architectures While writing this section on DBMS_APPLICATION_INFO, I had occasion to recommend the use of this package to help solve two real−world issues that came to my attention. In one case, an application had been written to call DBMS_SESSION.SET_SQL_TRACE and thus turn SQL tracing on for a session running the application.[1] The DBA wanted to know which sessions were being traced at any given time. I suggested the [Appendix A] What's on the Companion Disk? 7.3.7 Monitoring Application SQL Resource Consumption 384 use of DBMS_APPLICATION_INFO.SET_CLIENT_INFO to put a message into the V$SESSION table indicating a tracing session. The procedure to set tracing could look something like this: [1] Coding applications with the ability to set SQL tracing on and off is very good practice, as it can greatly assist in the detection of post−deployment runtime performance problems. PROCEDURE set_trace (on_TF IN BOOLEAN) IS BEGIN IF on_TF THEN DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TRACE ON'); ELSE DBMS_APPLICATION_INFO.SET_CLIENT_INFO(''); END IF; DBMS_SESSION.SET_SQL_TRACE(on_TF); END set_trace; In the second example, I was discussing with another DBA the difficult issue of tracking down specific users in the following types of applications: • Three−tier applications like Oracle WebServer where users do not connect to Oracle directly, but through proxy connections held by the application server. • Applications where all users connect to Oracle under a common username, and security and user−differentiation are maintained entirely within the application at runtime. Both of these architectures make it difficult for the DBA to correlate specific end users with the database sessions they are currently using. In the first case, sessions are persistent and serve different users at different times −− and sometimes no user at all. In the second case, all user sessions connect to a common username and thus are indistinguishable (by username) in V$SESSION. Interestingly enough, these are both perfect opportunities to use DBMS_APPLICATION_INFO.SET_CLIENT_INFO. When users connect to the application, call a procedure like the set_user procedure in the example for DBMS_APPLICATION_INFO.SET_CLIENT_INFO. A better version of set_user would call register_app.client_info to enable performance statistics tracking for the application users. 7.3.9 Tracking Long−Running Processes The SET_SESSION_LONGOPS procedure is an interesting addition to DBMS_APPLICATION_INFO first found in the Oracle8 version of the package. Oracle documentation makes it clear that the intended use of the procedure is to enable external tracking of the progress of long−duration operations through the new virtual table, V$SESSION_LONGOPS. However, I found SET_SESSION_LONGOPS rather nonintuitive and unwieldy to use. One difficult concept is the reuse of the four rows in V$SESSION_LONGOPS based on unique combinations of context and stepid, and how this relates to the hint parameter, which is used to identify the row to modify. Context and stepid do not have to be unique among the rows in V$SESSION_LONGOPS, but setting a new context/stepid combination will always cause acquisition of a new row. Because multiple rows can be identical in context/stepid, they do not really form a key (along with the session SID) to the virtual table. The hint parameter to DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS seems to be the only way to identify which row is currently being set, but there is no column in V$SESSION_LONGOPS corresponding to the hint. Thus it is actually impossible to externally identify with accuracy the row modified by the last call to the procedure. This defeated my efforts to write a READ_SESSION_LONGOPS procedure that takes a hint value in and reports the values for the row identified by that hint value. [Appendix A] What's on the Companion Disk? 7.3.9 Tracking Long−Running Processes 385 . to DBMS_APPLICATION_INFO. When covering an Oracle built−in package, it is good practice to create a package with an identical specification (or API) to that of the built−in. We can actually cover DBMS_APPLICATION_INFO. applications like Oracle WebServer where users do not connect to Oracle directly, but through proxy connections held by the application server. • Applications where all users connect to Oracle under. procedure is an interesting addition to DBMS_APPLICATION_INFO first found in the Oracle8 version of the package. Oracle documentation makes it clear that the intended use of the procedure is to