temp_return_code := DBMS_PIPE.RECEIVE_MESSAGE (pipename => request_pipe ,timeout => timeout_IN); IF temp_return_code = 0 THEN /* check if expected protocol */ DBMS_PIPE.UNPACK_MESSAGE(temp_protocol); IF temp_protocol = request_protocol THEN DBMS_PIPE.UNPACK_MESSAGE(request_rec_OUT.response_pipe); DBMS_PIPE.UNPACK_MESSAGE(request_rec_OUT.service); ELSE /* pipe message has unexpected protocol */ temp_return_code := −1; debug('UNKNOWN PROTOCOL: '||temp_protocol); DBMS_PIPE.RESET_BUFFER; END IF; END IF; return_code_OUT := temp_return_code; EXCEPTION WHEN OTHERS THEN return_code_OUT := SQLCODE; debug('RECEIVE REQUEST EXCP: '||SQLERRM ,force_TF_IN=>TRUE); DBMS_PIPE.RESET_BUFFER; END receive_unpack_request; Calls to RECEIVE_MESSAGE will wait for up to the value of the timeout parameter in seconds for the call to complete. Applications accessing database pipes that are usually empty of messages can incur lengthy wait times or timeouts. When using RECEIVE_MESSAGE under these circumstances, be careful to specify a timeout that users can tolerate. The RECEIVE_MESSAGE function will implicitly create a public pipe of the given name if one does not already exist. 3.1.6 Tips on Using DBMS_PIPE Oracle does not provide detailed documentation of exactly how database pipes work, nor much in the way of how best to use them. The programs in DBMS_PIPE are quite low−level utilities. Higher−level programs using DBMS_PIPE typically need to make numerous calls to these programs, which must be in the correct order, to handle communications. This can lead to complex and difficult code, unless a structured, modular, template−based approach is used. Through research and experience, I have adopted a method for building safe, reliable, and extensible higher−level communications layers on top of DBMS_PIPE. The following items are the main elements of this method: • Define message types using PL/SQL records • Encapsulate pack/send and receive/unpack logic around record types • [Appendix A] What's on the Companion Disk? 3.1.6 Tips on Using DBMS_PIPE 161 Separate messages using session−specific pipes • Use a well−defined protocol across applications • Pay attention to timeout values • Use RESET_BUFFER in exception handlers and pack/unpack routines • Take the time to size pipes correctly • Purge and remove pipes when finished 3.1.6.1 Defining message types and encapsulating communications logic PL/SQL records and DBMS_PIPE messages both bundle related data items together, so there is a natural affinity between them. When implementing applications that use DBMS_PIPE, do the following: 1. Determine the kinds of message data that will be communicated between sessions. 2. Develop a PL/SQL record type corresponding to each different type of message. 3. Build two procedures around the record type. One procedure takes a PL/SQL record and pipename as IN parameters, packs the record into a message using PACK_MESSAGE, and sends it to the pipe using SEND_MESSAGE. This procedure is usually named pack_send_rectype. The second procedure, usually called receive_unpack_rectype, performs the inverse operation. It takes a pipename as an IN parameter and a record as an OUT parameter, retrieves a message on the pipe using RECEIVE_MESSAGE, and unbundles the message into the record using calls to UNPACK_MESSAGE. 4. Once these two procedures are built, I can send and receive PL/SQL records as messages on database pipes; using these procedure calls, all of the low−level calls to DBMS_PIPE programs are hidden. This approach also makes it easy to extend the messaging to add new data items: simply add a new field to the end of the record type, and new calls to PACK_MESSAGE and UNPACK_MESSAGE to the two procedures. Examples of this record−to−pipe−message encapsulation technique can be seen in the examples for PACK_MESSAGE and UNPACK_MESSAGE, where procedures to pack/send and receive/unpack a record type called friend_rectype are defined. These examples are repeated here: /* Filename on companion disk: pipex1.sql */* TYPE friend_rectype IS RECORD (name VARCHAR2(60) ,birthdate DATE ,weight_lbs NUMBER ); friend_rec friend_rectype; [Appendix A] What's on the Companion Disk? 3.1.6 Tips on Using DBMS_PIPE 162 PROCEDURE pack_send_friend (friend_rec_IN IN friend_rectype ,pipename_IN IN VARCHAR2) IS call_status INTEGER; BEGIN /* ||notice the PACK_MESSAGE overloading */ DBMS_PIPE.PACK_MESSAGE(friend_rec_IN.name); DBMS_PIPE.PACK_MESSAGE(friend_rec_IN.birthdate); DBMS_PIPE.PACK_MESSAGE(friend_rec_IN.weight_lbs); call_status := DBMS_PIPE.SEND_MESSAGE (pipename=>pipename_IN,timeout=>0); IF call_status != 0 THEN DBMS_OUTPUT.PUT_LINE('Send message failed'); END IF; END pack_send_friend; PROCEDURE receive_unpack_friend (friend_rec_OUT OUT friend_rectype ,pipename_IN IN VARCHAR2) IS call_status INTEGER; BEGIN call_status := DBMS_PIPE.RECEIVE_MESSAGE (pipename=>pipename_IN,timeout=>0); /* ||NOTE: UNPACK_MESSAGE overloaded but we must || call the correct version */ DBMS_PIPE.UNPACK_MESSAGE(friend_rec_OUT.name); DBMS_PIPE.UNPACK_MESSAGE(friend_rec_OUT.birthdate); DBMS_PIPE.UNPACK_MESSAGE(friend_rec_OUT.weight_lbs); END receive_unpack_friend; 3.1.6.2 Separating messages Once a reliable mechanism is established for sending and receiving a specific message type, the next step is to make sure that messages get to their expected recipients. Database pipes themselves can hold any message, so separating messages of different types is usually done by creating specific pipes to handle specific types of messages. Messages intended for a specific user will usually be placed on a pipe whose name is unique to the user session. Unique pipenames can be established using the UNIQUE_SESSION_NAME function. Common pipes −− for example, those on which server programs listen for service requests −− will have pipenames known to all sessions that use the service. These names are typically contained in private global variables embedded in the packages that handle the DBMS_PIPE communications for the server. 3.1.6.3 Establishing messaging protocols Since any session with access to a database pipe can put a message there, it is good practice for programs that receive and unpack messages to establish a protocol to reliably identify the type of message received prior to unpacking. This protects against receiving ORA−06559 errors because the next item type in the message does not match the item parameter in the call to UNPACK_MESSAGE. It also protects against unpacking a message successfully, only to find out that the data itself was not really what was expected. Therefore, it is good practice for all messages to have a protocol identifier as the first item in the message. This item should [Appendix A] What's on the Companion Disk? 3.1.6 Tips on Using DBMS_PIPE 163 always be a specific datatype. I use and recommend VARCHAR2, because safer and more meaningful protocol identifiers than numbers can be created this way. Once protocol identifiers are established, receive/unpack routines will have code that looks like the following: call_status := DBMS_PIPE.RECEIVE_MESSAGE(pipename_IN, timeout_value); IF call_status = 0 THEN /* unpack protocol id */ DBMS_PIPE.UNPACK_MESSAGE(protocol_id); IF protocol_id = 'EXPECTED ID FOR THIS ROUTINE' THEN /* || OK, we know what message type, unpack the rest here || using calls to DBMS_PIPE.UNPACK_MESSAGE */ ELSE DBMS_PIPE.RESET_BUFFER; RAISE_APPLICATION_ERROR('Invalid protocol'); END IF; END IF; 3.1.6.4 Paying attention to timeouts Note also in the previous code fragment that a timeout is specified in the call to RECEIVE_MESSAGE, which is another of my recommended best practices. Sessions will block and wait for up to the value of the timeout parameter for calls to SEND_MESSAGE or RECEIVE_MESSAGE to complete. The default for this parameter is the constant DBMS_PIPE.maxwait, which equates to 1000 days! Since most users are not quite this patient, it pays to spend some time determining and using acceptable timeout values. 3.1.6.5 Using RESET_BUFFER The code fragment also illustrates one place where I use the RESET_BUFFER procedure: when a message has been received but the protocol is unrecognized. This effectively discards the message. Another place where the message buffer should be reset for safety is at the beginning of message packing routines. Resetting here ensures that the message packed does not include any items that may have been packed into the buffer prior to entering the routine (i.e., that the message is only what is intended). Finally, another good place to use RESET_BUFFER is in exception handlers for programs using DBMS_PIPE programs. Since exceptions indicate unexpected results, it is safe practice to make sure that the message buffer is initialized under these circumstances. 3.1.6.6 Sizing and removing pipes for good memory management Finally, my best practices call for proper sizing, purging, and removal of database pipes. These all amount to observing good memory management in the Oracle shared pool. Pipes that are too large may interfere with other shared pool operations. Pipes that are too small for their traffic volume can result in long wait times or timeouts in calls to SEND_MESSAGE. Pipes that are no longer needed and that still contain messages effectively waste SGA memory because they will not be aged out of the shared pool. Thus, it pays to spend time making pipe sizes large enough to handle traffic with minimum wait times, yet as small as possible to conserve memory, and to remove pipes or at least purge them of messages when they are no longer needed. 3.1.7 DBMS_PIPE Examples This section contains several longer examples of using DBMS_PIPE. [Appendix A] What's on the Companion Disk? 3.1.6 Tips on Using DBMS_PIPE 164 3.1.7.1 Communicating with the outside world One of the primary advantages of DBMS_PIPE is that it facilitates the long−desired ability to communicate with the "outside world" from within Oracle. In this new situation, database pipes can provide users access to external services from within their Oracle sessions. Oracle's package specification script for DBMS_PIPE (dbmspipe.sql ) presents a relatively complete example of how a "stock price request server" service could be implemented. Several examples of how to implement such external services using 3GL languages like C are available through online sources. In particular, Oracle Technical Support Bulletin 105688.158 gives a complete implementation of a daemon process written in C that listens on a database pipe and provides Oracle sessions with the ability to execute operating system commands from PL/SQL (similar to the HOST command in SQL*Plus). 3.1.7.2 Exploring DBMS_PIPE I had several questions about database pipes and decided to explore the following: • Can a message packed into the buffer be unpacked prior to sending? • Do PACK_MESSAGE and RECEIVE_MESSAGE use a common buffer? • Does the PACK_MESSAGE procedure do any data compression to conserve memory? I developed several tests to help answer these questions. The first question seemed quite straightforward. This is the test I developed and the results: DECLARE message_out VARCHAR2(2000); BEGIN /* try to pack and unpack */ DBMS_PIPE.PACK_MESSAGE('This is my message'); DBMS_PIPE.UNPACK_MESSAGE(message_out); DBMS_OUTPUT.PUT_LINE ('message unpacked: '||message_out); END; / DECLARE * ERROR at line 1: ORA−06556: the pipe is empty, cannot fulfill the unpack_message request ORA−06512: at "SYS.DBMS_PIPE", line 71 ORA−06512: at line 11 Calling UNPACK_MESSAGE immediately following a call to PACK_MESSAGE generated an exception, so it appears that the first answer is no: the packed message buffer cannot be unpacked prior to sending. However, check out the results of the following test: DECLARE message_out VARCHAR2(2000); BEGIN /* initialize buffer */ DBMS_PIPE.RESET_BUFFER; /* try to pack and unpack */ DBMS_PIPE.PACK_MESSAGE('This is my message'); [Appendix A] What's on the Companion Disk? 3.1.7 DBMS_PIPE Examples 165 . "outside world" from within Oracle. In this new situation, database pipes can provide users access to external services from within their Oracle sessions. Oracle& apos;s package specification. sources. In particular, Oracle Technical Support Bulletin 105688.158 gives a complete implementation of a daemon process written in C that listens on a database pipe and provides Oracle sessions with. create a public pipe of the given name if one does not already exist. 3.1.6 Tips on Using DBMS_PIPE Oracle does not provide detailed documentation of exactly how database pipes work, nor much in the