Oracle7 Server Application Developer's Guide

Contents Index Home Previous Next

Database Pipes

The DBMS_PIPE package allows two or more sessions in the same instance to communicate. Oracle pipes are similar in concept to the pipes used in UNIX, but Oracle pipes are not implemented using the operating system pipe mechanisms. 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.

Depending upon your security requirements, you may choose to use either a public pipe or a private pipe.

Attention: Pipes are independent of transactions. Be careful using pipes when transaction control can be affected.

Summary

Table 8 - 1 summarizes the procedures you can call in the DBMS_PIPE package.

Function/Procedure Description Refer to
CREATE_PIPE Explicitly create a pipe (necessary for private pipes). [*]
PACK_MESSAGE Build message in local buffer. [*]
SEND_MESSAGE Send message on named pipe. Implicitly create a public pipe if named pipe does not exist. [*]
RECEIVE_MESSAGE Copy message from named pipe into local buffer. [*]
NEXT_ITEM_TYPE Return datatype of next item in buffer. [*]
UNPACK_MESSAGE Access next item in buffer. [*]
REMOVE_PIPE Remove the named pipe. [*]
PURGE Purge contents of named pipe. [*]
RESET_BUFFER Purge contents of local buffer. [*]
UNIQUE_ SESSION_NAME Return unique session name. [*]
Table 8 - 1. DBMS_PIPE Package Functions and Procedures

Creating the DBMS_PIPE Package

To create the DBMS_PIPE package, submit the DBMSPIPE.SQL and PRVTPIPE.PLB scripts when connected as the user SYS. These scripts are run automatically by the CATPROC.SQL script. See [*] for information on granting the necessary privileges to users who will be executing this package.

Public Pipes

You can create a public pipe either implicitly or explicitly. For implicit public pipes, the pipe is automatically created when referenced for the first time, and it disappears when it no longer contains data. Because the pipe descriptor is stored in the SGA, there is some space usage overhead until the empty pipe is aged out of the cache.

You can create an explicit public pipe by calling the CREATE_PIPE function with the PRIVATE flag set to FALSE. You must deallocate explicitly-created pipes by calling the REMOVE_PIPE function.

The domain of a public pipe is the schema in which it was created, either explicitly or implicitly.

Writing and Reading

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 know the name of the public pipe.

Any schema user with the appropriate privileges and knowledge can read information from a public pipe. However, once buffered information is read by one user, it is emptied from the buffer, and is not available for other readers of the same pipe.

The sending session builds a message 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 procedure, 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 procedure, 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.

Private Pipes

You must explicitly create a private pipe by calling the CREATE_PIPE function. Once created, the private pipe persists in shared memory until you explicitly deallocate it by calling the REMOVE_PIPE function. A private pipe is also deallocated when the database instance is shut down.

You cannot create a private pipe if an implicit pipe exists in memory and has the same name as the private pipe you are trying to create. In this case CREATE_PIPE returns an error.

Access to a private pipe is restricted to the following:

An attempt by any other user to send or receive messages on the pipe, or to remove the pipe, results in an immediate error. Any attempt by another user to create a pipe with the same name also causes an error.

As with public pipes, you must first build your message using calls to PACK_MESSAGE before calling SEND_MESSAGE. Similarly you must call RECEIVE_MESSAGE to retrieve the message before accessing the items in the message by calling UNPACK_MESSAGE.

Errors

DBMS_PIPE package routines can return the following errors:

ORA-23321: Pipename may not be null
ORA-23322: Insufficient privilege to access pipe

ORA-23321 can be returned by CREATE_PIPE, or any subprogram that takes a pipe name as a parameter. ORA-23322 can be returned by any subprogram that references a private pipe in its parameter list.

CREATE_PIPE

Call CREATE_PIPE to explicitly create a public or private pipe. If the PRIVATE flag is TRUE, the pipe creator is assigned as the owner of the private pipe. Explicitly created pipes can only be removed by calling REMOVE_PIPE, or by shutting down the instance.

Warning: Do not use a pipe name beginning with ORA$; these names are reserved for use by Oracle Corporation.

Syntax

The parameters for the CREATE_PIPE function are shown in Table 8 - 2 and the possible return values and their meanings are described in Table 8 - 3. The syntax for this function is

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

Parameter Description
pipename Specify a name for the pipe that you are creating. You will need to use this name when you call SEND_MESSAGE and RECEIVE_MESSAGE. This name must be unique across the instance.
maxpipesize Specify the maximum size allowed 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 MAXPIPESIZE for a pipe becomes a part of the characteristics of the pipe and persists for the life of the pipe. Callers of SEND_MESSAGE with larger values cause the MAXPIPESIZE to be increased. Callers with a smaller value simply use the existing, larger value.
private Use the default, TRUE, to create a private pipe. Public pipes can be implicitly created when you call SEND_MESSAGE.
Return Value or Error Description
0 Indicates the pipe was successfully created.
If the pipe already exists and the user attempting to create it is authorized to use it, Oracle returns 0, indicating success, and any data already in the pipe remains.
If a user connected as SYSDBA/SYSOPER re-creates a pipe, Oracle returns status 0, but the ownership of the pipe remains unchanged.
ORA-23322 Indicates a failure due to naming conflict.
If a pipe with the same name exists and was created by a different user, Oracle signals error ORA-23322, indicating the naming conflict.
Table 8 - 3. DBMS_PIPE.CREATE_PIPE Function Return Values

PACK_MESSAGE Procedures

To send a message, first make one or more calls to PACK_MESSAGE to build your message in the local message buffer. Then 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. In addition to the data bytes, each item in the buffer requires one byte to indicate its type, and two bytes to store its length. One additional byte is needed to terminate the message. If the message buffer exceeds 4096 bytes, Oracle raises exception ORA-6558.

When you call SEND_MESSAGE to send this message, you must indicate the name of the pipe on which you want to send the message. If this pipe already exists, you must have sufficient privileges to access this pipe. If the pipe does not already exist, it is created automatically.

Warning: Do not use a pipe name beginning with ORA$; these names are reserved for use by Oracle Corporation.

Syntax

The syntax for the PACK_MESSAGE procedures is shown below. Note that the UNPACK_MESSAGE procedure itself is overloaded to accept items of type VARCHAR2, NUMBER, or DATE. There are two additional procedures to pack RAW and ROWID items.

DBMS_PIPE.PACK_MESSAGE       (item  IN  VARCHAR2);
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

The parameters for the SEND_MESSAGE function are shown in Table 8 - 4 and the possible return values and their meanings are described in Table 8 - 5. The syntax for this function is shown below.

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

Parameter Description
pipename Specify the name of the pipe on which you want to place the message. If you are using an explicit pipe, this is the name that you specified when you called CREATE_PIPE.
timeout Specify the timeout period in seconds. This is the time to wait while attempting to place a message on the pipe; the return values are explained below. The default value is the constant MAXWAIT, which is defined as 86400000 (1000 days).
maxpipesize Specify the maximum size allowed 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 MAXPIPESIZE for a pipe becomes a part of the characteristics of the pipe and persists for the life of the pipe. Callers of SEND_MESSAGE with larger values cause the MAXPIPESIZE to be increased. Callers with a smaller value simply use the existing, larger value. Specifying MAXPIPESIZE as part of the SEND_MESSAGE procedure eliminates the need for a separate call to open the pipe. If you created the pipe explicitly, you can use the optional MAXPIPESIZE parameter to override the creation pipe size specification.
Table 8 - 4. DBMS_PIPE.SEND_MESSAGE Function Parameters

Return Value or Error Description
0 Indicates the pipe was successfully created.
If the pipe already exists and the user attempting to create it is authorized to use it, Oracle returns 0, indicating success, and any data already in the pipe remains.
If a user connected as SYSDBA/SYSOPER re-creates a pipe, Oracle returns status 0, but the ownership of the pipe remains unchanged.
1 Indicates the pipe has timed out. This procedure can timeout either because it cannot get a lock on the pipe, or because the pipe remains too full to be used. If the pipe was implicitly created and is empty, it is removed.
3 Indicates an interrupt has occurred. If the pipe was implicitly created and is empty, it is removed.
ORA-23322 Indicates insufficient privileges to write to the pipe.
If a pipe with the same name exists and was created by a different user, Oracle signals error ORA-23322, indicating the naming conflict.
Table 8 - 5. DBMS_PIPE.SEND_MESSAGE Function Return Values

RECEIVE_MESSAGE

To receive a message from a pipe, first call RECEIVE_MESSAGE to copy the message into the local message buffer. When you receive a message, it is removed from the pipe; that is, a message can only be received once. For implicitly created pipes, the pipe is removed after the last record is removed from the pipe.

If the pipe that you specify when you call RECEIVE_MESSAGE does not already exist, Oracle implicitly creates the pipe and then waits to receive the message. If the message does not arrive within a designated timeout interval, the call returns and the pipe is removed.

After receiving the message, you must make one or more calls to UNPACK_MESSAGE to access the individual items in the message. The UNPACK_MESSAGE procedure is overloaded to unpack items of type DATE, NUMBER, VARCHAR2, and there are two additional procedures to unpack RAW and ROWID items. If you do not know the type of data that you are attempting to unpack, you can call NEXT_ITEM_TYPE to determine the type of the next item in the buffer.

Syntax

The parameters for the RECEIVE_MESSAGE function are shown in Table 8 - 6 and the possible return values and their meanings are described in Table 8 - 7. The syntax for this function is shown below.

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

Parameter Description
pipename Specify the name of the pipe on which you want to receive a message. Names beginning with ORA$ are reserved for use by Oracle.
timeout Specify the timeout period in seconds. This is the time to wait to receive a message on the pipe. The default value is the constant MAXWAIT, which is defined as 86400000 (1000 days). A timeout of 0 allows you to read without blocking.
Table 8 - 6. DBMS_PIPE.RECEIVE_MESSAGE Function Parameters

Return Value or Error Description
0 Indicates the message was received successfully.
1 Indicates the pipe has timed out. If the pipe was implicitly created and is empty, it is removed.
2 Indicates the record in the pipe is too large for the buffer. (This should not happen.)
3 Indicates an interrupt has occurred.
ORA-23322 Indicates the user has insufficient privileges to read from the pipe.
Table 8 - 7. DBMS_PIPE.RECEIVE_MESSAGE Function Return Values

NEXT_ITEM_TYPE

After you have called RECEIVE_MESSAGE to place pipe information in a local buffer, you can call NEXT_ITEM_TYPE to determine the datatype of the next item in the local message buffer. When NEXT_ITEM_TYPE returns 0, the local buffer is empty.

Syntax

The possible return values and their meanings for the NEXT_ITEM_TYPE function are described in Table 8 - 8. The syntax for this function is shown below.

DBMS_PIPE.NEXT_ITEM_TYPE RETURN INTEGER; 

Return Value Description
0 no more items
6 NUMBER
9 VARCHAR2
12 DATE
Table 8 - 8. DBMS_PIPE.NEXT_ITEM_TYPE Function Return Values

UNPACK_MESSAGE Procedures

After you have called RECEIVE_MESSAGE to place pipe information in a local buffer, you call UNPACK_MESSAGE to retrieve items from the buffer.

Syntax

The syntax for the UNPACK_MESSAGE procedures is shown below. Note that the UNPACK_MESSAGE procedure is overloaded to return items of type VARCHAR2, NUMBER, or DATE. There are two additional procedures to unpack RAW and ROWID items.

DBMS_PIPE.UNPACK_MESSAGE       (item  OUT VARCHAR2);
DBMS_PIPE.UNPACK_MESSAGE       (item  OUT NUMBER);
DBMS_PIPE.UNPACK_MESSAGE       (item  OUT DATE);
DBMS_PIPE.UNPACK_MESSAGE_RAW   (item   IN DATE);
DBMS_PIPE.UNPACK_MESSAGE_ROWID (item   IN DATE);

If the message buffer contains no more items, or if the item received is not of the same type as that requested, the ORA-2000 exception is raised.

REMOVE_PIPE

Pipes created implicitly by SEND_MESSAGE are automatically removed when empty.

Pipes created explicitly by CREATE_PIPE are removed only by calling REMOVE_PIPE or when the instance is shut down. All unconsumed records in the pipe are removed before the pipe is deleted. This is similar to calling PURGE on an implicitly created pipe.

Syntax

The REMOVE_PIPE function accepts only one parameter--the name of the pipe that you want to remove. The possible return values and their meanings are described in Table 8 - 9. The syntax for this function is

DBMS_PIPE.REMOVE_PIPE(pipename  IN  VARCHAR2)
RETURN INTEGER;

Return Value or Error Description
0 Indicates the pipe was successfully removed.
If the pipe does not exist, or if the pipe already exists and the user attempting to remove it is authorized to do so, Oracle returns 0, indicating success, and any data remaining in the pipe is removed.
ORA-23322 Indicates a failure due to insufficient privileges.
If the pipe exists, but the user is not authorized to access the pipe, Oracle signals error ORA-23322, indicating insufficient privileges.
Table 8 - 9. DBMS_PIPE.REMOVE_PIPE Function Return Values

Managing Pipes

The DBMS_PIPE package contains additional procedures and functions that you might find useful.

Purging the Contents of a Pipe

Call PURGE to empty the contents of a pipe. An empty implicitly created pipe is aged out of the shared global area according to the least-recently-used algorithm. Thus, calling PURGE lets you free the memory associated with an implicitly created pipe.

Because PURGE calls RECEIVE_MESSAGE, the local buffer might be overwritten with messages as they are purged from the pipe. Also, you can receive an ORA-23322, insufficient privileges, error if you attempt to purge a pipe to which you have insufficient access rights.

DBMS_PIPE.PURGE(pipename  IN  VARCHAR2);

Resetting the Message Buffer

Call RESET_BUFFER to reset the PACK_MESSAGE and UNPACK_MESSAGE positioning indicators to 0. Because all pipes share a single buffer, you may find it useful to reset the buffer before using a new pipe. This ensures that the first time you attempt to send a message to your pipe, you do not inadvertently send an expired message remaining in the buffer.

Syntax

The syntax for the RESET_BUFFER procedure is shown below.

DBMS_PIPE.RESET_BUFFER; 

Getting a Unique Session Name

Call UNIQUE_SESSION_NAME to receive a name that is unique among all of the sessions that are currently connected to a database. Multiple calls to this function from the same session always return the same value. The return value can be up to 30 bytes. You might find it useful to use this function to supply the PIPENAME parameter for your SEND_MESSAGE and RECEIVE_MESSAGE calls.

DBMS_PIPE.UNIQUE_SESSION_NAME RETURN VARCHAR2;

Example 1: Debugging

The following example shows a procedure a PL/SQL program can call to place debugging information in a pipe:

CREATE OR REPLACE PROCEDURE debug (msg VARCHAR2) AS
  status  NUMBER;
BEGIN
  dbms_pipe.pack_message(LENGTH(msg));
  dbms_pipe.pack_message(msg);
  status := dbms_pipe.send_message('plsql_debug');
  IF status != 0 THEN
    raise_application_error(-20099, 'Debug error');
  END IF;
END debug;

This example shows the Pro*C code that receives messages from the PLSQL_DEBUG pipe in the PL/SQL example above, and displays the messages. If the Pro*C session is run in a separate window, it can be used to display any messages that are sent to the debug procedure from a PL/SQL program executing in a separate session.

#include <stdio.h> 
#include <string.h> 
 
EXEC SQL BEGIN DECLARE SECTION; 
   VARCHAR username[20]; 
   int     status; 
   int     msg_length;
   char    retval[2000];
EXEC SQL END DECLARE SECTION; 
 
EXEC SQL INCLUDE SQLCA; 
 
void sql_error(); 
 
main() 
{ 
/* prepare username */ 
   strcpy(username.arr, "SCOTT/TIGER"); 
   username.len = strlen(username.arr); 
 
   EXEC SQL WHENEVER SQLERROR DO sql_error(); 
   EXEC SQL CONNECT :username; 
 
   printf("connected\n"); 
 
/* start an endless loop to look for and print 
   messages on the pipe */ 
   for (;;) 
   { 
      EXEC SQL EXECUTE 
         DECLARE 
            len INTEGER;
            typ INTEGER;
            sta INTEGER; 
            chr VARCHAR2(2000); 
         BEGIN 
            chr := ''; 
            sta := dbms_pipe.receive_message('plsql_debug'); 
            IF sta = 0 THEN 
               dbms_pipe.unpack_message(len);
               dbms_pipe.unpack_message(chr); 
            END IF; 
            :status := sta; 
            :retval := chr; 
            IF len IS NOT NULL THEN
               :msg_length := len;
            ELSE
               :msg_length := 2000;
            END IF;
         END; 
      END-EXEC; 
      if (status == 0) 
         printf("\n%.*s\n", msg_length, retval);
      else 
         printf("abnormal status, value is %d\n", status); 
   }
}


void sql_error() 
{ 
   char msg[1024]; 
   int rlen, len; 
   len = sizeof(msg); 
   sqlglm(msg, &len, &rlen); 
   printf("ORACLE ERROR\n"); 
   printf("%.*s\n", rlen, msg); 
   exit(1); 
} 

Example 2: Execute System Commands

The following example shows PL/SQL and Pro*C code that can let a PL/SQL stored procedure (or anonymous block) call PL/SQL procedures to send commands over a pipe to a Pro*C program that is listening for them.

The Pro*C program just sleeps, waiting for a message to arrive on the named pipe. When a message arrives, the C program processes it, carrying out the required action, such as executing a UNIX command through the system() call, or executing a SQL command using embedded SQL.

DAEMON.SQL is the source code for the PL/SQL package. This package contains procedures that use the DBMS_PIPE package to send and receive message to and from the Pro*C daemon. Note that full handshaking is used. The daemon will always send a message back to the package (except in the case of the 'STOP' command). This is valuable, since it allows the PL/SQL procedures to be sure that the Pro*C daemon is running.

You can call the DAEMON packaged procedures from an anonymous PL/SQL block using SQL*Plus or Server Manager. For example:

SVRMGR> variable rv number
SVRMGR> execute :rv := DAEMON.EXECUTE_SYSTEM('ls -la');

would, on a UNIX system, cause the Pro*C daemon to execute the command system("ls -la").

Remember that the daemon needs to be running first. So you might want to run it in the background, or in another window beside the SQL*Plus or Server Manager session from which you call it.

The DAEMON.SQL also uses the DBMS_OUTPUT package (see [*]) to display the results. For this example to work, you must have execute privileges on this package.

DAEMON.SQL

This is the code for the PL/SQL DAEMON package:

CREATE OR REPLACE PACKAGE daemon AS
  FUNCTION execute_sql(command VARCHAR2, 
                       timeout NUMBER DEFAULT 10)
    RETURN NUMBER;

  FUNCTION execute_system(command VARCHAR2,
                          timeout NUMBER DEFAULT 10)
    RETURN NUMBER;

  PROCEDURE stop(timeout NUMBER DEFAULT 10);
END daemon;
/
CREATE OR REPLACE PACKAGE BODY daemon AS

  FUNCTION execute_system(command VARCHAR2,
                          timeout NUMBER DEFAULT 10)
  RETURN NUMBER IS

    status       NUMBER;
    result       VARCHAR2(20);
    command_code NUMBER;
    pipe_name    VARCHAR2(30);
  BEGIN
    pipe_name := DBMS_PIPE.UNIQUE_SESSION_NAME;

    DBMS_PIPE.PACK_MESSAGE('SYSTEM');
    DBMS_PIPE.PACK_MESSAGE(pipe_name);
    DBMS_PIPE.PACK_MESSAGE(command);
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20010,
        'Execute_system: Error while sending.  Status = ' ||
         status);
    END IF;

    status := DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20011,
        'Execute_system: Error while receiving. 
         Status = ' || status);
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(result);
    IF result <> 'done' THEN
      RAISE_APPLICATION_ERROR(-20012,
        'Execute_system: Done not received.');
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(command_code);
    DBMS_OUTPUT.PUT_LINE('System command executed.  result = ' ||
                         command_code);
    RETURN command_code;
  END execute_system;

  FUNCTION execute_sql(command VARCHAR2,
                       timeout NUMBER DEFAULT 10)
  RETURN NUMBER IS

    status       NUMBER;
    result       VARCHAR2(20);
    command_code NUMBER;
    pipe_name    VARCHAR2(30);

  BEGIN
    pipe_name := DBMS_PIPE.UNIQUE_SESSION_NAME;

    DBMS_PIPE.PACK_MESSAGE('SQL');
    DBMS_PIPE.PACK_MESSAGE(pipe_name);
    DBMS_PIPE.PACK_MESSAGE(command);
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20020,
        'Execute_sql: Error while sending.  Status = ' || status);
    END IF;

    status := DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);


    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20021,
        'execute_sql: Error while receiving.  
         Status = ' || status);
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(result);
    IF result <> 'done' THEN
      RAISE_APPLICATION_ERROR(-20022,
        'execute_sql: done not received.');
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(command_code);
    DBMS_OUTPUT.PUT_LINE
        ('SQL command executed.  sqlcode = ' || command_code);
    RETURN command_code;
  END execute_sql;

  PROCEDURE stop(timeout NUMBER DEFAULT 10) IS
    status NUMBER;
  BEGIN
    DBMS_PIPE.PACK_MESSAGE('STOP');
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20030,
        'stop: error while sending.  status = ' || status);
    END IF;
  END stop;
END daemon;

daemon.pc

This is the code for the Pro*C daemon. You must precompile this using the Pro*C Precompiler, Version 1.5.x or later. You must also specify the USERID and SQLCHECK options, as the example contains embedded PL/SQL code. For example:

proc iname=daemon userid=scott/tiger sqlcheck=semantics

Then C-compile and link in the normal way.

#include <stdio.h>
#include <string.h>

EXEC SQL INCLUDE SQLCA;




EXEC SQL BEGIN DECLARE SECTION;
  char *uid = "scott/tiger";
  int status;
  VARCHAR command[20];
  VARCHAR value[2000];
  VARCHAR return_name[30];
EXEC SQL END DECLARE SECTION;

void
connect_error()
{
  char msg_buffer[512];
  int msg_length;
  int buffer_size = 512;

  EXEC SQL WHENEVER SQLERROR CONTINUE;
  sqlglm(msg_buffer, &buffer_size, &msg_length);
  printf("Daemon error while connecting:\n");
  printf("%.*s\n", msg_length, msg_buffer);
  printf("Daemon quitting.\n");
  exit(1);
}

void
sql_error()
{
  char msg_buffer[512];
  int msg_length;
  int buffer_size = 512;

  EXEC SQL WHENEVER SQLERROR CONTINUE;
  sqlglm(msg_buffer, &buffer_size, &msg_length);
  printf("Daemon error while executing:\n");
  printf("%.*s\n", msg_length, msg_buffer);
  printf("Daemon continuing.\n");
}
main()
{
  EXEC SQL WHENEVER SQLERROR DO connect_error();
  EXEC SQL CONNECT :uid;
  printf("Daemon connected.\n");

  EXEC SQL WHENEVER SQLERROR DO sql_error();
  printf("Daemon waiting...\n");
  while (1) {
    EXEC SQL EXECUTE
      BEGIN
        :status := DBMS_PIPE.RECEIVE_MESSAGE('daemon');
        IF :status = 0 THEN
          DBMS_PIPE.UNPACK_MESSAGE(:command);
        END IF;
      END;
    END-EXEC;
    if (status == 0)
    {
      command.arr[command.len] = '\0';
      if (!strcmp((char *) command.arr, "STOP"))
      {
        printf("Daemon exiting.\n");
        break;
      }

      else if (!strcmp((char *) command.arr, "SYSTEM"))
      {
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.UNPACK_MESSAGE(:return_name);
            DBMS_PIPE.UNPACK_MESSAGE(:value);
          END;
        END-EXEC;
        value.arr[value.len] = '\0';
        printf("Will execute system command '%s'\n", value.arr);

        status = system(value.arr);
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.PACK_MESSAGE('done');
            DBMS_PIPE.PACK_MESSAGE(:status);
            :status := DBMS_PIPE.SEND_MESSAGE(:return_name);
          END;
        END-EXEC;

        if (status)
        {
          printf
           ("Daemon error while responding to system command.");
          printf("  status: %d\n", status);
        }
      }
      else if (!strcmp((char *) command.arr, "SQL")) {
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.UNPACK_MESSAGE(:return_name);
            DBMS_PIPE.UNPACK_MESSAGE(:value);
          END;
        END-EXEC;
        value.arr[value.len] = '\0';
        printf("Will execute sql command '%s'\n", value.arr);

        EXEC SQL WHENEVER SQLERROR CONTINUE;
        EXEC SQL EXECUTE IMMEDIATE :value;
        status = sqlca.sqlcode;

        EXEC SQL WHENEVER SQLERROR DO sql_error();
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.PACK_MESSAGE('done');
            DBMS_PIPE.PACK_MESSAGE(:status);
            :status := DBMS_PIPE.SEND_MESSAGE(:return_name);
          END;
        END-EXEC;

        if (status)
        {
          printf("Daemon error while responding to sql command.");
          printf("  status: %d\n", status);
        }
      }
      else
      {
        printf
          ("Daemon error: invalid command '%s' received.\n",
            command.arr);
      }
    }
    else
    {
      printf("Daemon error while waiting for signal.");
      printf("  status = %d\n", status);
    }
  }
  EXEC SQL COMMIT WORK RELEASE;
  exit(0);
}


Contents Index Home Previous Next