,private_IN IN BOOLEAN DEFAULT TRUE RETURN BOOLEAN; /* || encapsulates DBMS_PIPE.REMOVE_PIPE and returns || FALSE if ORA−23322 is raised, indicating || the pipename exists and is not
Trang 1,private_IN IN BOOLEAN DEFAULT TRUE)
RETURN BOOLEAN;
/*
|| encapsulates DBMS_PIPE.REMOVE_PIPE and returns
|| FALSE if ORA−23322 is raised, indicating
|| the pipename exists and is not removable
|| by the caller
*/
FUNCTION closepipe
(pipename_IN IN VARCHAR2)
RETURN BOOLEAN;
/*
|| purges all pipes the caller can access
*/
PROCEDURE purge_all_pipes;
END dbpipe;
3.1.7.3.1 Unpack_to_tbl and pack_from_tbl procedures
The two procedures unpack_to_tbl and pack_from_tbl implement the generic unpack and pack functionality They use PL/SQL tables of records based on message_tbltype, which is designed to hold an ordered list of items of any datatype Each row in a table of type message_tbltype contains two data values: an entry in the item_type field indicating the type of this message item (as returned by DBMS_PIPE.NEXT_ITEM_TYPE) and an entry in the field of the corresponding datatype with the value of this message item The unpack_to_tbl procedure unpacks all items in a newly received message into a message table, indexing them in the table by their unpack order The pack_from_tbl procedure takes a message table loaded in this fashion and repacks the original message into the message buffer in index order The unpack_to_tbl procedure can also optionally use the DBMS_OUTPUT built−in package (described in Chapter 6, Generating Output from PL/SQL Programs)
to display the message unpacked.
Here are the full package bodies for unpack_to_tbl and pack_from_tbl Note how unpack_to_tbl grew out of the example code for the DBMS.PIPE.NEXT_ITEM_TYPE function.
/* Filename on companion disk: dbpipe.sql */*
PROCEDURE unpack_to_tbl
(message_tbl_OUT OUT message_tbltype
,display_TF IN BOOLEAN := FALSE)
IS
/*
|| NOTE: this procedure should only be called after
|| a successful call to DBMS_PIPE.RECEIVE_MESSAGE
*/
/* empty table to flush output table on exception */
null_message_tbl message_tbltype;
/*
|| temp display variable extra long to account
|| for RAWTOHEX conversion that can double size
*/
temp_varchar2 VARCHAR2(8186);
next_item INTEGER;
item_count INTEGER := 0;
BEGIN
next_item := DBMS_PIPE.NEXT_ITEM_TYPE;
/*
Trang 2|| loop through all items, unpacking each by item
|| type and convert to varchar2 for display
*/
WHILE next_item > 0
LOOP
/*
|| increment item count and store item type
*/
item_count := item_count + 1;
message_tbl_OUT(item_count).item_type := next_item;
/*
|| now use next_item to call correct unpack procedure,
|| saving item to message_tbl
||
|| also stuff temp_varchar2 with string conversion
|| of the item
*/
IF next_item = 9
THEN
DBMS_PIPE.UNPACK_MESSAGE
(message_tbl_OUT(item_count).Mvarchar2);
temp_varchar2 := 'VARCHAR2: '||
message_tbl_OUT(item_count).Mvarchar2;
ELSIF next_item = 6
THEN
DBMS_PIPE.UNPACK_MESSAGE
(message_tbl_OUT(item_count).Mnumber);
temp_varchar2 := 'NUMBER: '||
TO_CHAR(message_tbl_OUT(item_count).Mnumber);
ELSIF next_item = 11
THEN
DBMS_PIPE.UNPACK_MESSAGE_ROWID
(message_tbl_OUT(item_count).Mrowid);
temp_varchar2 := 'ROWID: '||
ROWIDTOCHAR(message_tbl_OUT(item_count).Mrowid);
ELSIF next_item = 12
THEN
DBMS_PIPE.UNPACK_MESSAGE
(message_tbl_OUT(item_count).Mdate);
temp_varchar2 := 'DATE: '||
TO_CHAR(message_tbl_OUT(item_count).Mdate,
'YYYY:MM:DD:HH24:MI:SS');
ELSIF next_item = 23
THEN
DBMS_PIPE.UNPACK_MESSAGE_RAW
(message_tbl_OUT(item_count).Mraw);
temp_varchar2 := 'RAW: '||
RAWTOHEX(message_tbl_OUT(item_count).Mraw);
ELSE
temp_varchar2 := 'Invalid item type: '||
TO_CHAR(next_item);
RAISE invalid_item_type;
END IF;
/*
|| display results and get next item type
Trang 3*/
IF display_TF
THEN
DBMS_OUTPUT.PUT_LINE(temp_varchar2);
END IF;
next_item := DBMS_PIPE.NEXT_ITEM_TYPE;
END LOOP;
EXCEPTION
WHEN invalid_item_type
THEN
message_tbl_OUT := null_message_tbl;
END unpack_to_tbl;
PROCEDURE pack_from_tbl
(message_tbl_IN IN message_tbltype)
IS
/*
|| packs the session message buffer from a generic
|| message table
*/
BEGIN
FOR i IN message_tbl_IN.FIRST message_tbl_IN.LAST
LOOP
IF message_tbl_IN(i).item_type = 9
THEN
DBMS_PIPE.PACK_MESSAGE(message_tbl_IN(i).Mvarchar2);
ELSIF message_tbl_IN(i).item_type = 6
THEN
DBMS_PIPE.PACK_MESSAGE(message_tbl_IN(i).Mnumber);
ELSIF message_tbl_IN(i).item_type = 12
THEN
DBMS_PIPE.PACK_MESSAGE(message_tbl_IN(i).Mdate);
ELSIF message_tbl_IN(i).item_type = 11
THEN
DBMS_PIPE.PACK_MESSAGE_ROWID(message_tbl_IN(i).Mrowid);
ELSIF message_tbl_IN(i).item_type = 23
THEN
DBMS_PIPE.PACK_MESSAGE_RAW(message_tbl_IN(i).Mraw);
END IF;
END LOOP;
ENDpack_from_tbl;
I really like these utilities, but they suffer from a potentially serious limitation inherited from Oracle's rather poor memory management for PL/SQL tables of records Basically, each row of a PL/SQL table of type message_tbltype consumes at least enough memory to fill out the variable−length columns, which is greater than eight kilobytes Thus, unpacking a message with more than a few items in it can result in a very large PL/SQL table This is demonstrated by the following test results, which use the my_session.memory
procedure (see Chapter 11, Managing Session Information) to display user session memory before and after unpacking a message.
/* Filename on companion disk: pipemem.sql */*
set serveroutput on size 100000
Trang 4null_msg_tbl dbpipe.message_tbltype;
msg_tbl dbpipe.message_tbltype;
call_stat INTEGER;
BEGIN
/* pack a message with a number of items */
FOR i in 1 50
LOOP
DBMS_PIPE.PACK_MESSAGE('message number: '||TO_CHAR(i));
END LOOP;
/* send and receive the message */
call_stat :=DBMS_PIPE.SEND_MESSAGE('PIPEX');
call_stat :=DBMS_PIPE.RECEIVE_MESSAGE('PIPEX');
/* use the generic unpack and show memory */
dbpipe.unpack_to_tbl(msg_tbl,FALSE);
my_session.memory;
/* now free, release and show memory */
msg_tbl := null_msg_tbl;
DBMS_SESSION.FREE_UNUSED_USER_MEMORY;
my_session.memory;
END;
/
session UGA: 41160
session PGA: 987576
session UGA: 41160
session PGA: 137760
PL/SQL procedure successfully completed
The test shows that using unpack_to_tbl on a message with 50 items results in session PGA memory
exceeding 900 kilobytes in size, most of which is wasted Clearly, this is not a good scenario for a real
application with many users, so the general usefulness of unpack_to_tbl and pack_from_tbl will have to wait until Oracle fixes these PL/SQL memory management problems.
NOTE: The problem caused by unpacking messages with more than a few items in them can
result in a very large PL/SQL table that has been fixed in Oracle PL/SQL8.
3.1.7.3.2 The peek procedure
Developers or DBAs working with and testing DBMS_PIPE applications may really like the peek procedure built on top of the generic pack and unpack procedures The peek procedure lets you pull a message off any pipe (which you have permission to use), look at its content, and place it back into the pipe, if you desire Note that using peek will change the message order in the pipe, since database pipes are FIFO queues.
/* Filename on companion disk: dbpipe.sql */*
PROCEDURE peek
(pipename_IN IN VARCHAR2
,timeout_secs_IN IN INTEGER := 60
,replace_message_TF IN BOOLEAN := TRUE)
IS
/*
|| Takes a sample message from a pipe, unpacks and displays
|| contents using unpack_to_tbl procedure
||
|| If replace_message_TF parameter is TRUE (the default),
|| then the message is replaced into the pipe.NOTE: this
|| will change message order in the pipe
Trang 5*/
message_tblmessage_tbltype;
call_statusINTEGER;
/* empty table used to free and release memory */
null_message_tblmessage_tbltype;
BEGIN
call_status := DBMS_PIPE.RECEIVE_MESSAGE
(pipename=>pipename_IN, timeout=>timeout_secs_IN);
IF call_status = 0
THEN
unpack_to_tbl(message_tbl, display_TF=>TRUE);
IF replace_message_TF
THEN
/*
|| repack message into initialized buffer
*/
DBMS_PIPE.RESET_BUFFER;
pack_from_tbl(message_tbl);
/*
|| replace message on the pipe
*/
call_status := DBMS_PIPE.SEND_MESSAGE
(pipename=>pipename_IN, timeout=>0);
END IF;
/*
|| empty message_tbl and free memory
*/
message_tbl := null_message_tbl;
DBMS_SESSION.FREE_UNUSED_USER_MEMORY;
END IF;
END peek;
The peek procedure takes the memory management limitations into account It returns memory consumed by the unpack_to_tbl procedure to the operating system by initializing message_tbl and calling
DBMS_SESSION.FREE_UNUSED_USER_MEMORY.
3.1.7.3.3 The forward procedure
The final fun utility from dbpipe to be discussed here is the forward procedure, which lets you forward a message from one pipe to another The procedure has four IN parameters:
from_pipename_IN and to_pipename_IN
Receiving and sending pipes for the message forwarding.
timeout_secs_IN
Determines the number of seconds to wait for a message to forward (on the pipe from_pipename_IN).
safe_mode_IN
A Boolean that determines which of two message forwarding techniques to use (which I call "safe"
and "cool") Safe mode forwarding uses unpack_to_tbl and pack_from_tbl to physically unbundle and recreate the message before sending it on to_pipename_IN Cool mode forwarding is based on the
idea that the best way to forward a message should be to execute