SET_LABEL SET_MLS_LABEL_FORMAT SET_NLS SET_ROLE These programs are "high−level," in that they would normally be called directly from an application program and not be buried deep inside layers of PL/SQL code. In fact, the SET_ROLE procedure can only be called from anonymous PL/SQL blocks and not from within stored program (procedures and functions) code. So in practice, an application would begin by prompting the user for preferences, issue the appropriate DBMS_SESSION.SET procedure calls, and then move on to the real work. Other programs in DBMS_SESSION are geared toward manipulating session−level resource utilization, particularly memory. In this category are the following DBMS_SESSION programs: SET_CLOSE_CACHED_OPEN_CURSORS CLOSE_DATABASE_LINK FREE_UNUSED_USER_MEMORY RESET_PACKAGE These are also quite "high−level" routines, but more likely to find their way into application code under the right circumstances. One thing that DBMS_SESSION does not have (and that it should) is a function to return the current session id. This is frequently asked for by developers and DBAs and is relatively easy to provide. NOTE: The source code for all of the examples is in a file called mysess.sql, which creates the package called my_session shown in this section that includes these examples. 11.1.5.1 Adding value to DBMS_SESSION Let's take a look at how we can use DBMS_SESSION and add a little value along the way. I've created a package called my_session to do just that. Here is the package specification: /* Filename on companion disk: mysess.sql */* CREATE OR REPLACE PACKAGE my_session /* || Extends some of the functionality of DBMS_SESSION || and provides access to additional session−level || information. || || Author: John Beresniewicz, Savant Corp || || 12/22/97: exposed load_unique_id as/per Phil Pitha || 09/07/97: modified function SID to assert WNPS || and not call load_my_session_rec || 07/27/97: created || || Compilation Requirements: || || SELECT on sys.v_$session || SELECT on sys.v_$sesstat || SELECT on sys.v_$statname || || Execution Requirements: || || ALTER SESSION */ AS /* same as DBMS_SESSION.UNIQUE_SESSION_ID but callable in SQL */ FUNCTION unique_id RETURN VARCHAR2; [Appendix A] What's on the Companion Disk? 11.1.5 DBMS_SESSION Examples 531 PRAGMA RESTRICT_REFERENCES(unique_id, WNDS,WNPS); /* || loads unique_session_id into global variable, must be called || prior to using function unique_id */ PROCEDURE load_unique_id; /* returns session id of current session, callable in SQL */ FUNCTION sid RETURN NUMBER; PRAGMA RESTRICT_REFERENCES(sid,WNDS,WNPS); /* closes any open database links not in use */ PROCEDURE close_links (force_with_commit_TF IN BOOLEAN DEFAULT FALSE); /* loads session data, should be private but needs to assert purity */ PROCEDURE load_my_session_rec; PRAGMA RESTRICT_REFERENCES(load_my_session_rec,WNDS); /* resets package states and frees memory */ PROCEDURE reset; /* returns current stat value from V$SESSTAT for this session */ FUNCTION statval(statname_IN IN VARCHAR2) RETURN NUMBER; /* displays session uga and pga using DBMS_OUTPUT */ PROCEDURE memory; /* turns SQL tracing on/off with tag for file identification */ PROCEDURE set_sql_trace (trace_TF IN BOOLEAN ,tag_IN IN VARCHAR2 DEFAULT USER); END my_session; You will notice that several of the programs seem very similar to programs in DBMS_SESSION. Well, they are, but with some important differences. 11.1.5.2 The unique_id function Prior to Oracle 7.3.3, the DBMS.SESSION.UNIQUE_SESSION_ID function did not assert any purity level using the RESTRICT_REFERENCES pragma and thus could not be called directly from SQL statements. This is unfortunate, because one nice potential use of the function is as an identifier for applications making use of shared temporary tables. In other words, some applications will find it useful to do things like the following: INSERT INTO temp_table (session_id, other_columns ) VALUES (DBMS_SESSION.UNIQUE_SESSION_ID, other_columns ); DELETE FROM temp_table WHERE session_id = DBMS_SESSION.UNIQUE_SESSION_ID; Thankfully, Oracle Corporation has corrected this shortcoming in the latest releases of DBMS_SESSION. For those not fortunate enough to be using 7.3.3 or 8.0, the my_session.unique_id function can be used as a workaround. This function returns the same string as DBMS_SESSION.UNIQUE_SESSION_ID, yet it asserts a purity level of WNDS and can thus be called from SQL. Here is the source to unique_id and its companion procedure load_unique_id: /* Filename on companion disk: mysess.sql */ /* private global to hold DBMS_SESSION.UNIQUE_SESSION_ID */ [Appendix A] What's on the Companion Disk? 11.1.5 DBMS_SESSION Examples 532 unique_id_ VARCHAR2(40); /* || loads unique_session_id into global variable, must be called || prior to using function unique_id */ PROCEDURE load_unique_id IS BEGIN unique_id_ := DBMS_SESSION.UNIQUE_SESSION_ID; END load_unique_id; /* || returns unique_id_ loaded by call to load_unique_id */ FUNCTION unique_id RETURN VARCHAR2 IS BEGIN RETURN unique_id_; END unique_id; As you can see, unique_id simply returns the value of a private package global variable that is set by the load_unique_id procedure to the value returned by DBMS_SESSION.UNIQUE_SESSION_ID. The only caveat is that load_unique_id must be called in the session prior to calling unique_id (or a NULL value will be returned). Note that using a private global and function is safer than using a public global, since the public global cannot be protected from inadvertent modification. 11.1.5.3 The load_my_session_rec procedure Each session has a unique row in the V$SESSION virtual table with various columns containing identification and activity information about the session. The load_my_session_rec procedure selects the row in V$SESSION corresponding to the current session and loads it into a package global record called my_session_rec. /* Filename on companion disk: mysess.sql */* /* || my_session_cur and my_session_rec are both declared || to always hold all columns of V$SESSION */ CURSOR my_session_cur IS SELECT * FROM sys.v_$session WHERE audsid = USERENV('SESSIONID'); my_session_rec sys.v_$session%ROWTYPE; /* || loads V$SESSION data into global record for current session */ PROCEDURE load_my_session_rec IS BEGIN OPEN my_session_cur; FETCH my_session_cur INTO my_session_rec; CLOSE my_session_cur; END load_my_session_rec; Notice that load_my_session_rec is written in a way that ensures it always gets all columns of V$SESSION. This is accomplished by anchoring the package global my_session_rec to V$SESSION using %ROWTYPE in the declaration. Similarly, the cursor my_session_cur used to fetch into my_session_rec is anchored to [Appendix A] What's on the Companion Disk? 11.1.5 DBMS_SESSION Examples 533 V$SESSION by using the SELECT * syntax. This is a nice technique. Since V$SESSION can change with Oracle versions, writing the procedure in this way allows it to adjust itself to the particular version of Oracle under which it is executing. 11.1.5.4 The sid function Several of the Oracle dynamic performance (V$) views are keyed by session id because they contain session−level performance data. Many developers and DBAs have had to answer the question "What is my current sid?" when delving into these performance tables. I don't know why DBMS_SESSION does not come with a sid function, but my_session sure does. Here is the relevant source code: /* Filename on companion disk: mysess.sql */ /* /* || returns the session id of current session */ FUNCTION sid RETURN NUMBER temp_session_rec sys.v_$session%ROWTYPE; BEGIN IF my_session_rec.sid IS NULL THEN OPEN my_session_cur; FETCH my_session_cur INTO temp_session_rec; CLOSE my_session_cur; ELSE temp_session_rec := my_session_rec; END IF; RETURN temp_session_rec.sid; END sid; The sid function itself is quite simple, yet it has a subtle but important performance optimization. Since the session id will never change for the duration of the session, it is necessary to load it only once, and this can be done using the load_my_session_rec procedure. The IF statement checks to see if we've already loaded the my_session_rec.sid and bypasses opening my_session_cur in that case. Remember that we intend to use the function in SQL statements, and it will be executed for every row returned in which the function is referenced. That simple IF statement could save hundreds (or even thousands) of scans on the V$SESSION view per SQL statement. Be sure to execute load_my_session_rec before using the sid function to avoid the unnecessary performance penalty. We can use the my_session.sid function to view our current performance statistics from V$SESSTAT as follows: SELECT n.name,s.value FROM v$statname n, v$sesstat s WHERE n.statistic# = s.statistic# AND s.sid = my_session.sid; NOTE: Astute readers may ask: Why not just call load_my_session_rec from the sid function if my_session_rec.sid has not been initialized? Well, originally this is exactly the way sid was written. However, since load_my_session_rec modifies package state, this meant that WNPS (Writes No Package State) purity could not be asserted for the sid function. In order to use a PL/SQL function in the WHERE clause of a SQL statement, the function must assert WNPS, so sid had to be modified to not call load_my_session_rec. 11.1.5.5 The close_links procedure The Oracle initialization parameter OPEN_LINKS controls the maximum number of concurrent open connections to remote databases per user session. When a session exceeds this number, the following Oracle error is raised: [Appendix A] What's on the Companion Disk? 11.1.5 DBMS_SESSION Examples 534 ORA−02020 Too many database links in use. Generally, the database administrator should set the OPEN_LINKS parameter to a value that will accommodate the needs of distributed applications accessing the database. However, in highly distributed environments with multiple applications, it's possible that users could receive the ORA−02020 error. Presumably, this is the purpose of the CLOSE_DATABASE_LINK procedure; however, there is a serious problem. Quite simply, users should not have to know anything about database links nor, for that matter, should applications. Database links are an implementation detail that should be kept transparent to users and applications. The real question is: When an ORA−02020 is incurred, how is a user or application supposed to know which links are open and can be closed? Well, it's my opinion that users should not have to know about specific database links and yet should be able to do something in case of an ORA−02020 error. That is precisely the purpose of my_session.close_links. That procedure will close any open links that are not in use and can be closed. A link is considered in use if it has been referenced within the current transaction (i.e., since the last COMMIT or ROLLBACK). Alternatively, close_links will close all open links by issuing a COMMIT to terminate the current transaction and free all links for closure. /* Filename on companion disk: mysess.sql */* /* || closes all open database links not in use by session, || or all if forced_with_commit_TF is TRUE */ PROCEDURE close_links (force_with_commit_TF IN BOOLEAN DEFAULT FALSE) IS /* declare exceptions for ORA errors */ dblink_in_use EXCEPTION; PRAGMA EXCEPTION_INIT(dblink_in_use,−2080); dblink_not_open EXCEPTION; PRAGMA EXCEPTION_INIT(dblink_not_open,−2081); /* cursor of all db links available to user */ CURSOR all_links_cur IS SELECT db_link FROM all_db_links; BEGIN /* try all links and close the ones you can */ FOR dblink_rec IN all_links_cur LOOP BEGIN DBMS_SESSION.CLOSE_DATABASE_LINK(dblink_rec.db_link); EXCEPTION WHEN dblink_not_open THEN null; WHEN dblink_in_use THEN IF force_with_commit_TF THEN COMMIT; DBMS_SESSION.CLOSE_DATABASE_LINK(dblink_rec.db_link); END IF; WHEN OTHERS THEN null; END; END LOOP; [Appendix A] What's on the Companion Disk? 11.1.5 DBMS_SESSION Examples 535 . with Oracle versions, writing the procedure in this way allows it to adjust itself to the particular version of Oracle under which it is executing. 11.1.5.4 The sid function Several of the Oracle. Oracle initialization parameter OPEN_LINKS controls the maximum number of concurrent open connections to remote databases per user session. When a session exceeds this number, the following Oracle error. DBMS_SESSION. Well, they are, but with some important differences. 11.1.5.2 The unique_id function Prior to Oracle 7.3.3, the DBMS.SESSION.UNIQUE_SESSION_ID function did not assert any purity level using