Reading data across sessions by using DBMS_PIPE package

As we know Oracle uses PGA (Program Global Area) for execution of PL/SQL programs and SGA (System Global Area) for maintaining other data for a specific session. Several times it is required to share the value of a variable across different sessions. Normally when we use a variable, Oracle de-allocates it as soon as the session ends and at the same time the value of the variable is accessible through out the session only. But what if we want to use the value of the same variable across several sessions for the same instance? The answer is the DBMS_PIPE package.

The DBMS_PIPE package lets two or more sessions in the same instance to communicate with each other. Information sent through Oracle pipes is buffered in the system global area (SGA). All information in pipes is lost when the instance is shut down. 

Pipes are of two types:
· Public Pipes
· Private Pipes 

Public Pipes 

We can create a public pipe either implicitly or explicitly. An implicit public pipe is automatically created when it is referenced for the first time, and it disappears when it no longer contains data. 

A public explicit pipe can be created by calling the CREATE_PIPE function with the private flag set to FALSE. We have to de-allocate explicitly-created pipes by calling the REMOVE_PIPE function. 

Private Pipes

We can explicitly create a private pipe by calling the CREATE_PIPE function. Once created, the private pipe persists in shared memory until we explicitly de-allocate it by calling the REMOVE_PIPE function. A private pipe is also de-allocated when the database instance is shut down. However we cannot create a private pipe if an implicit pipe exists in memory and has the same name as the private pipe we are trying to create. In this case, CREATE_PIPE returns an error. 

Access to a private pipe is restricted to: 
· Sessions running under the same User ID as the creator of the pipe 
· Stored subprograms executing in the same User ID privilege domain as the pipe creator 
· Users connected as SYSDBA or INTERNAL 

The Approach

The sending session builds a message by using one or more calls to the PACK_MESSAGE procedure. This procedure adds the message to the session's local message buffer. The information in this buffer is sent by calling the SEND_MESSAGE function, designating the pipe name to be used to send the message. When SEND_MESSAGE is called, all messages that have been stacked in the local buffer are sent. 

A process that wants to receive a message calls the RECEIVE_MESSAGE function, designating the pipe name from which to receive the message. The process then calls the UNPACK_MESSAGE procedure to access each of the items in the message. 

Each public pipe works asynchronously. Any number of schema users can write to a public pipe, as long as they have EXECUTE permission on the DBMS_PIPE package, and they know the name of the public pipe. To grant this privilege to your schema user simple execute the following SQL grant statement after connecting as SYS:

GRANT EXECUTE ON DBMS_PIPE TO <user_name>

However, once buffered information is read by one user by using RECEIVE_MESSAGE and then unpacked by UNPACK_MESSAGE, it is emptied from the buffer, and is not available for other readers of the same pipe. 

Pipe Uses

The pipe functionality has several potential applications: 
· External service interface: We can communicate with user-written services that are external to the RDBMS. This can be done in a (effectively) multi-threaded manner, so that several instances of the service are executing simultaneously. Additionally, the services are available asynchronously. The requestor of the service does not need to block a waiting reply. The requestor can check (with or without timeout) at a later time. 
· Independent transactions: The pipe can communicate to a separate session which can perform an operation in an independent transaction.
· Debugging: Triggers and stored procedures can send debugging information to a pipe. Another session can keep reading out of the pipe and display it on the screen or write it to a file. 
· Concentrator: This is useful for multiplexing large numbers of users over a fewer number of network connections, or improving performance by concentrating several user-transactions into one DBMS transaction. 

Procedures and Function defined in DBMS_PIPE Package
If we describe the DBMS_PIPE package we can get the following information:

SQL> desc dbms_pipe

FUNCTION CREATE_PIPE RETURNS NUMBER(38)

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 PIPENAME                       VARCHAR2                IN

 MAXPIPESIZE                    NUMBER(38)              IN     DEFAULT

 PRIVATE                        BOOLEAN                 IN     DEFAULT

 

FUNCTION NEXT_ITEM_TYPE RETURNS NUMBER(38)

PROCEDURE PACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           VARCHAR2                IN

 

PROCEDURE PACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           NUMBER                  IN

 

PROCEDURE PACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           DATE                    IN

 

PROCEDURE PACK_MESSAGE_RAW

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           RAW                     IN

 

PROCEDURE PACK_MESSAGE_ROWID

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           ROWID                   IN

 

ROCEDURE PURGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 PIPENAME                       VARCHAR2                IN

 

FUNCTION RECEIVE_MESSAGE RETURNS NUMBER(38)

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 PIPENAME                       VARCHAR2                IN

 TIMEOUT                        NUMBER(38)              IN     DEFAULT

 

 

FUNCTION REMOVE_PIPE RETURNS NUMBER(38)

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 PIPENAME                       VARCHAR2                IN

 

PROCEDURE RESET_BUFFER

 

FUNCTION SEND_MESSAGE RETURNS NUMBER(38)

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 PIPENAME                       VARCHAR2                IN

 TIMEOUT                        NUMBER(38)              IN     DEFAULT

 MAXPIPESIZE                    NUMBER(38)              IN     DEFAULT

 



FUNCTION UNIQUE_SESSION_NAME RETURNS VARCHAR2

 

PROCEDURE UNPACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           VARCHAR2                OUT

 

PROCEDURE UNPACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           NUMBER                  OUT

 

PROCEDURE UNPACK_MESSAGE

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           DATE                    OUT

 

PROCEDURE UNPACK_MESSAGE_RAW

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           RAW                     OUT

 

PROCEDURE UNPACK_MESSAGE_ROWID

 Argument Name                  Type                    In/Out Default?

 ------------------------------ ----------------------- ------ --------

 ITEM                           ROWID                   OUT

......................................................................................................................................................


Although each of the above mentioned items is having their specific usage, but for the time being we will concentrate only some of them:

CREATE_PIPE function

This function explicitly creates a public or private pipe. If the private flag is TRUE, then the pipe creator is assigned as the owner of the private pipe. 

Syntax:
DBMS_PIPE.CREATE_PIPE (
pipename IN VARCHAR2,
maxpipesize IN INTEGER DEFAULT 8192,
private IN BOOLEAN DEFAULT TRUE)
RETURN INTEGER;


Here pipename is the name of the pipe which we are creating. We must use this name when we are calling SEND_MESSAGE and RECEIVE_MESSAGE. This name must be unique across the instance. 
The maxpipesize is the size for the pipe, in bytes.The total size of all of the messages on the pipe cannot exceed this amount. The message is blocked if it exceeds this maximum. The default maxpipesize is 8192 bytes. 
The private uses the default, TRUE, to create a private pipe

PACK_MESSAGE procedure
This procedure builds our message in the local message buffer. To send a message, first we have to make one or more calls to PACK_MESSAGE. Then, we have to call SEND_MESSAGE to send the message in the local buffer on the named pipe. 
The PACK_MESSAGE procedure is overloaded to accept items of type VARCHAR2, NUMBER, or DATE. When we call SEND_MESSAGE to send this message, we must indicate the name of the pipe on which we want to send the message. If this pipe already exists, then we must have sufficient privileges to access this pipe. If the pipe does not already exist, then it is created automatically. 

Syntax:
DBMS_PIPE.PACK_MESSAGE (item IN VARCHAR2);
DBMS_PIPE.PACK_MESSAGE (item IN NCHAR);
DBMS_PIPE.PACK_MESSAGE (item IN NUMBER);
DBMS_PIPE.PACK_MESSAGE (item IN DATE);
DBMS_PIPE.PACK_MESSAGE_RAW (item IN RAW);
DBMS_PIPE.PACK_MESSAGE_ROWID (item IN ROWID);


SEND_MESSAGE function

This function sends a message on the named pipe. The message is contained in the local message buffer, which was filled with calls to PACK_MESSAGE. A pipe could be explicitly using CREATE_PIPE; otherwise, it is created implicitly. 

Syntax:
DBMS_PIPE.SEND_MESSAGE (
pipename IN VARCHAR2,
timeout IN INTEGER DEFAULT MAXWAIT,
maxpipesize IN INTEGER DEFAULT 8192)
RETURN INTEGER;


The usage of the parameters pipename and maxpipesize are same as mentioned in the CREATE_PIPE function. The only new parameter is timeout, which is the time to wait while attempting to place a message on a pipe, in seconds. The default value is the constant MAXWAIT, which is defined as 86400000 (1000 days). 

The SEND_MESAGE returns 0 for success, 1 if Timed out, 3 if an interrupt occurred and ORA-23322 if the user who is trying to send the message not having sufficient privilege to do so.

RECEIVE_MESSAGE function

This function copies the message into the local message buffer. To receive a message from a pipe, first we have to call RECEIVE_MESSAGE. When we receive a message, it is removed from the pipe; hence, a message can only be received once. 

Syntax:
DBMS_PIPE.RECEIVE_MESSAGE (
pipename IN VARCHAR2,
timeout IN INTEGER DEFAULT maxwait)
RETURN INTEGER;


All the parameters and values returned are same as discussed previously. 

UNPACK_MESSAGE procedure

This procedure retrieves items from the buffer. 

Syntax:
DBMS_PIPE.UNPACK_MESSAGE (item OUT VARCHAR2);
DBMS_PIPE.UNPACK_MESSAGE (item OUT NCHAR);
DBMS_PIPE.UNPACK_MESSAGE (item OUT NUMBER);
DBMS_PIPE.UNPACK_MESSAGE (item OUT DATE);
DBMS_PIPE.UNPACK_MESSAGE_RAW (item OUT RAW);
DBMS_PIPE.UNPACK_MESSAGE_ROWID (item OUT ROWID);



Here I am giving a small example on how to use the DBMS_PIPE package. The sample schema named SCOTT I am using is provided by Oracle at the time of database creation. The example I am giving is tested on Oracle 8.1.6.0.0.

In the example I want to fix a rate of increment in their salary for the employees working in an organization. The same rate should be used throughout all the sessions which have connected Oracle for updating the salary of the employees.

Connect Oracle with scott/tiger and run the following code snippet.

-- Creation of emp_inc_rate package
create or replace package emp_inc_rate
as 
sal_inc_per number(5,2) :=0;
procedure set_inc_rate(yr in number);
procedure get_per;
end;
/


Please do note here a package description has been created which has defined two procedures and a variable. The first procedure called set_inc_rate will fix the rate of the increment in the salary depending upon the year passed as an argument. The rate fixed will be available in the sal_inc_per variable which the second procedure get_per will read and update the salaries accordingly. Although both the procedures are in same package we can directly use the sal_inc_per variable, but strictly if it is running in the same session. For example if session A has assigned the sal_inc_per to 10% then normally it will not be available for the session B. For session B it will be 0 as its default assigned value. Using DBMS_PIPE can solve the purpose which will allow us to access the data throughout different sessions also.

Here I am giving the actual implementation of the package. Run the following code snippet.

create or replace package body emp_inc_rate
as
procedure set_inc_rate(yr in number) as
cur_yr number(4):=yr; 
pm integer;
sm integer;
begin
if mod(cur_yr,5)=0
then
sal_inc_per:=10;
else
sal_inc_per:=5;
end if;

pm := dbms_pipe.create_pipe('sal_inc',8192,true);
dbms_output.put_line('pipe creation was : '|| pm);

dbms_pipe.pack_message(sal_inc_per);
dbms_output.put_line('message packed with increment percentage of ' || sal_inc_per);

sm :=dbms_pipe.send_message('sal_inc',60,8192);
dbms_output.put_line('sending message was ' || sm);

end set_inc_rate;

procedure get_per 
as
cur_per number(5,2);
pm integer;
begin
pm :=dbms_pipe.receive_message('sal_inc',60);
dbms_output.put_line('receiving message was ' || pm);

dbms_pipe.unpack_message(cur_per);
dbms_output.put_line('current increment rate is fixed to ' || cur_per);

dbms_output.put_line('incrementing the salaries');
update emp set sal=sal+(sal*(cur_per/100));
dbms_output.put_line('salaries updated');
end get_per;
end;
/

The first procedure set_inc_rate has fixed the rate of interest depending upon the year passed to it. For example if the year is passed is 5th year of any decade then the increment will be 10% otherwise it will be only 5%. The increment percentage is assigned to the sal_inc_per variable. After that it has created a pipe called sal_inc and packed the same increment percentage and sent it. 
The second procedure get_per received the incremented rate and unpacked the information. It stored the message in a variable call cur_per and updated the salaries accordingly.

Please also note at the time of sending the message using the SEND_MESSAGE procedure as well as at the time of receiving the message using the RECEIVE_MESSAGE, the waiting time is set to 60, which means it will wait the send as well as to receive the message for 60 seconds (1 minute)

How to Use
Connect an Oracle sessions by scott user account. Run the following commands in one of the sessions (let us call it session A): 

SQL> set serveroutput on
SQL> set feedback off
SQL> execute emp_inc_rate.set_inc_rate(2005)


Oracle responds like this:

pipe creation was : 0
message packed with increment percentage of 10
sending message was 0

Here 0 indicates all the executions were successful. If any exception occurs you will be getting the related exception number.

Connect another Oracle session by the same user account (scott) and run the following commands:

SQL> set serveroutput on
SQL> set feedback off
SQL> Select ename, sal from emp where rownum<3;


Oracle responds like this:

ENAME SAL 
------------------------------ -------------
ADAMS 5000.00
BLAKE 10000.00


The output may differ depending upon the rows in the emp table. Now execute the following command:

SQL> execute emp_inc_rate.get_per

Oracle responds like this:

receiving message was : 0
current increment rate is fixed to 10
incrementing the salaries
salaries updated

Now check the emp table by passing the following command:

SQL> Select ename, sal from emp where rownum<3;

Oracle responds like this:

ENAME SAL 
------------------------------ -------------
ADAMS 5500.00
BLAKE 11000.00




Added on December 4, 2007 Comment

Comments

Post a comment