/* || private program to encapsulate request processing || logic (lots of IF THEN stuff) of server */ PROCEDURE process_request (request_rec_IN IN request_rectype) IS temp_return_code NUMBER; BEGIN /* increment total */ status_rec.total_requests := status_rec.total_requests +1; /* stop the server, this is a MUST have */ IF request_rec_IN.service = stop_req THEN terminate_TF := TRUE; ELSIF request_rec_IN.service = debugon_req THEN debug_TF := TRUE; status_rec.debug_status := 'ON'; ELSIF request_rec_IN.service = debugoff_req THEN debug_TF := FALSE; status_rec.debug_status := 'OFF'; ELSIF request_rec_IN.service = status_req THEN pack_send_status (status_rec_IN=>status_rec ,response_pipe_IN=> request_rec_IN.response_pipe ,return_code_OUT=> temp_return_code); debug('SEND STATUS: '||temp_return_code); /* unrecognized request */ ELSE DBMS_PIPE.RESET_BUFFER; debug('UNKNOWN REQUEST: '||request_rec_IN.service); END IF; END process_request; 3.1.7.5.6 Debug procedure Notice that the server procedure makes several calls to a procedure called debug. The debug procedure dumps informational messages into a simple table to allow tracking of server−side events. This procedure usually inserts data to the table only if the server is in debug mode, as determined by the debug_TF global variable. This prevents too many debugging rows from being inserted when the server is operating normally. The debug procedure can be forced to write to the table by setting a parameter called force_TF_IN to TRUE. This is used to guarantee that certain debugging information gets into the table regardless of the server's current debug mode. Debugging messages are usually forced in exception handlers, as in the server procedure. Here is the source code for debug: /* Filename on companion disk: pipesvr.sql */* /* || private program to put debug messages into table || if boolean is TRUE, or if force_IN is TRUE || NOTE: commits after inserting row */ PROCEDURE debug (message_IN IN VARCHAR2 ,force_TF_IN IN BOOLEAN := FALSE) IS [Appendix A] What's on the Companion Disk? 3.1.7 DBMS_PIPE Examples 181 BEGIN IF debug_TF OR force_TF_IN THEN INSERT INTO pipesvr_debug VALUES (SYSDATE, message_IN); COMMIT; END IF; END debug; 3.1.7.5.7 Client side procedures The four client−side procedures all have to send specific request records to the server, and the request records they use vary only in the contents of the service field. I reduced code redundancy by creating the client_request procedure as follows: /* Filename on companion disk: pipesvr.sql. */* /* || private program to make simple service requests, || if request_rectype gets more complex or need more || flexibility use pack_send_request instead */ PROCEDURE client_request(request_IN IN stop_req%TYPE) IS request_rec request_rectype; temp_return_code NUMBER; BEGIN request_rec.service := request_IN; pack_send_request (request_rec_IN => request_rec ,return_code_OUT => temp_return_code); END client_request; The client_request procedure loads the service field of a request record and then calls pack_send_request. This procedure helps simplify client programs, as seen in server_stop: PROCEDURE server_stop IS BEGIN client_request(stop_req); END server_stop; The server_status procedure calls client_request and then waits for up to a minute on the receive_unpack_status procedure. If a status record is successfully received from the server, it is displayed using the DBMS_OUTPUT package. I hope the pipesvr package will serve as a useful template to those seeking to implement PL/SQL service programs. It works and it incorporates the best practices for using DBMS_PIPE, so it should be a good starting point. 2.5 DBMS_SQL Examples 3.2 DBMS_ALERT: Broadcasting Alerts to Users [Appendix A] What's on the Companion Disk? 3.1.7 DBMS_PIPE Examples 182 Copyright (c) 2000 O'Reilly & Associates. All rights reserved. [Appendix A] What's on the Companion Disk? 3.1.7 DBMS_PIPE Examples 183 Chapter 3 Intersession Communication 3.2 DBMS_ALERT: Broadcasting Alerts to Users The DBMS_ALERT package provides a facility to broadcast notification of database events (alerts) to multiple users who have previously registered their interest in receiving those alerts. You will use the DBMS_ALERT package to implement applications that respond immediately to data modifications of interest to the application. In this way, you can avoid the need to do regular polling on the data to determine if changes have taken place. This is typically accomplished by having the application register to receive an alert on the specific data of interest, querying the data to establish a baseline, and then waiting for the alert to be signaled, which indicates the need to requery the data. Alerts can be automatically signaled using database triggers on the tables of interest, so that all modifications to the data will signal the alert, regardless of which application or user modified the data. Alerts are asynchronous and transaction−based, meaning that users can wait for and receive notification after the signaling event and that only committed transactions (usually involving data changes) will signal the alert. Here are two good examples of applications that could be implemented using DBMS_ALERT: • Graphical displays of statistics that must be updated whenever the underlying data changes • An online auction where bidders want to be notified when they have been outbid on an item 3.2.1 Getting Started with DBMS_ALERT The DBMS_ALERT package is created when the Oracle database is installed. The dbmsalrt.sql script (found in the built−in packages source code directory, as described in Chapter 1) contains the source code for this package's specification. This script is called by catproc.sql, which is normally run immediately after database creation. Under Oracle7, no privileges are automatically granted on DBMS_ALERT. Under Oracle8, the EXECUTE_CATALOG_ROLE role is granted EXECUTE privilege on DBMS_ALERT. Thus the DBMS_ALERT programs are not generally available to users. Access to DBMS_ALERT is obtained by granting EXECUTE privilege explicitly to users or roles that require use of the package. Note also that a public synonym for DBMS_ALERT is not created automatically by dbmsalrt.sql, so references to the package's programs must be qualified by the owning schema (SYS), unless synonyms have been created. To create a public synonym for DBMS_ALERT, issue the following SQL command: CREATE PUBLIC SYNONYM DBMS_ALERT FOR SYS.DBMS_ALERT; 3.2.1.1 DBMS_ALERT programs Table 3.2 lists the programs included in the DBMS_ALERT package. Table 3.2: DBMS_ALERT Programs 184 Name Description Use in SQL? REGISTER Registers interest in notification of an alert No REMOVE Unregisters interest in notification of an alert No REMOVEALL Unregisters interest in all alert notification No SET_DEFAULTS Sets polling loop interval No SIGNAL Signals the occurrence of an alert No WAITANY Waits for any registered alerts to occur No WAITONE Waits for a specific registered alert to occur No DBMS_ALERT does not declare any package exceptions of its own. Many of the individual programs raise Oracle exceptions under certain circumstances, as described in the following sections. 3.2.1.2 DBMS_ALERT nonprogram elements The DBMS_ALERT package contains one nonprogram element, maxwait. It is defined as follows: maxwait CONSTANT INTEGER := 86400000; The maxwait constant is the maximum time to wait for an alert. It is used as the default value for the timeout parameter in the WAITONE and WAITANY procedures. The value of 86400000 seconds corresponds to 1000 days. 3.2.2 The DBMS_ALERT Interface This section describes the programs available through the DBMS_ALERT package. 3.2.2.1 The DBMS_ALERT.REGISTER procedure The REGISTER procedure registers interest in a specific alert by a database session. Once registered, the session will be notified of any occurrences of the alert. The header for this procedure is, PROCEDURE DBMS_ALERT.REGISTER (name IN VARCHAR2); where name is the name of the alert to register for notification. 3.2.2.1.1 Exceptions The REGISTER procedure does not raise any package exceptions. It will raise an ORA−20000 exception for specific error conditions, with message text indicating the error as follows: ORU−10021 Lock request error; status: n ORU−10025 Lock request error; status: n 3.2.2.1.2 Restrictions Note the following restrictions on calling REGISTER: • [Appendix A] What's on the Companion Disk? 3.2.1 Getting Started with DBMS_ALERT 185 . Started with DBMS_ALERT The DBMS_ALERT package is created when the Oracle database is installed. The dbmsalrt.sql script (found in the built−in packages source code directory, as described in Chapter. which is normally run immediately after database creation. Under Oracle7 , no privileges are automatically granted on DBMS_ALERT. Under Oracle8 , the EXECUTE_CATALOG_ROLE role is granted EXECUTE privilege. No DBMS_ALERT does not declare any package exceptions of its own. Many of the individual programs raise Oracle exceptions under certain circumstances, as described in the following sections. 3.2.1.2 DBMS_ALERT