Storing Program Values

When running in a multi-user environment, programs called by CALLPGM may be multi-threaded. If so, data returned to the server must be returned in dynamically allocated storage, and the program must know how to retrieve the address of that storage. This is illustrated in the sample code in the following subsections.

Programs called by CALLPGM typically return the following data to the server:

A program returns data by placing it in an address (pointer) area.

Address area space allocations are by default 1024-bytes, which may suffice in some applications. An application may acquire dynamic storage on its own using those facilities of the language that are available for use within any given language on any given operating system or by issuing explicit commands to have the calling process (the server) set specific address area allocations for the called program to use.

To have the server set specific address allocations, use one or more of the following commands

SQL SPG SET SPGALLOC_CRT n    (SQL SPG SET CPGUB NEW ONLY)
SQL SPG SET SPGALLOC_MSG n 
SQL SPG SET SPGALLOC_ANS n

where n is a number between 1024 (1K) and 32768 (32K). The allocated address is then placed into the respective control block pointer location for the CALLPGM program to use.

To have the application itself acquire dynamic storage depending upon your environment use features such as:

It is the program's responsibility to free such storage at its last invocation.

It may also be necessary for subsequent invocations of a program to retrieve previously stored values, which would also require the use of dynamically acquired storage method.

By placing the address of the storage in the control block fields message_area and answer_area, the server returns the values to you on the next call, and then re-addresses the variables. Always point the message_area and answer_area to valid data when control is returned to the server.

The examples in the following sections show how values are saved across invocations of a program. The first time a program is called, it allocates dynamic storage for the values to be saved. Each subsequent time the program is called, the address of the dynamic storage is retrieved using the message_area or answer_area.

Sample programs are supplied with your software in locations as described below.

Type of Program

Supplied As

C

The sample is stored in hlq.HOME.ETC (CPT) for PDS deployment.

All other platforms are: cpt.c in the etc/src3gl directory of EDAHOME

COBOL

The samples are stored in hlq.HOME.ETC (SPGOLD) and hlq.HOME.ETC (SPGNEW) for PDS deployment.

All other platforms are, respectively:

  • SPGOLD.CBL in the etc/src3gl directory of EDAHOME
  • SPGNEW.CBL in the etc/src3gl directory of EDAHOME

RPG

(IBM i only)

  • SPGOLD.RPG in the etc/src3gl directory of EDAHOME
  • SPGNEW.RPG in the etc/src3gl directory of EDAHOME

The portable COBOL examples have specifically been tested with IBM Enterprise COBOL V3R2, HP OpenVMS COBOLv2.7, and IBM IBM i ILE COBOL. Depending on the target platform, minor editing (for example, commenting or un-commenting of lines) is required for use. Specific instructions are contained as comments at the beginning of the file.

The supplied samples work by parsing the parameters passed to the program and passing back information such as a number of records to return. None of the samples use actual database access; they simulate what and how to send data and messages back to the calling process using arbitrary text, therefore they need little in the way of setup for demonstration purposes. The samples all contain comments on requirements for compilation and use.



Example: Storing Program Values in C

The following sample C code illustrates the allocation of dynamic storage on the first call, and addressability to program variables on subsequent calls.

typedef struct message_buffer
   { char     message[80] ;
   } message_buffer;
 
 typedef struct answer_tuple
   { char     customer_name[40]    ;
     char     customer_address[90] ;
     char     balance_due[20]      ;
     char     comments[300]        ;
   } answer_tuple;
 
 typedef struct answer_buffer
   { int                  *program_variable_buffer_ptr ;
     struct answer_tuple   answer_set_tuple            ;
   } answer_buffer;
 
 typedef struct program_variable_buffer
   { long     number_of_rows     ;
     long     last_record        ;
     short    reserved           ;
     short    close_pending_flag ;
   } program_variable_buffer;
                      .
                      .
                      .
                      .
                      .
                      .
/* On the first call, allocate message, answer, and program variable  */
 /* buffers and anchor them in the input control block. The program's  */
 /* local variables are anchored by saving a pointer immediately       */
 /* preceding the answer_area. The pointer saved in the answer_area is */
 /* actually 4 bytes into the answer_buffer, providing the correct     */
 /* interface to the server for processing answer set requests,        */
 /* while still anchoring the program's local variables by "hiding" the*/
 /* pointer in the memory immediately preceding the answer_area. By    */
 /* placing the pointer before the answer_set_tuple, it is not seen    */
 /* by the server.                                                     */
 /*                                                                    */
 /* Check for first call of this program.                              */
 if ( flag_value = CPGUB_flag_first )
 
 {  /* Allocate answer_buffer.                                         */
    answer_buffer_ptr = ( answer_buffer * ) 
                malloc(sizeof(answer_buffer), 1);
 
    answer_area = ( int * ) ( ((long) (answer_buffer_ptr)) + 4 );
    answer_length = sizeof(answer_buffer) - 4;
 
    /* Allocate buffer for program variables.                          */
    program_variable_buffer_ptr = ( int * )
                malloc(sizeof(program_variable_buffer), 1);
 
    /* Allocate buffer for messages.                                   */
    message_area = ( int * ) malloc(sizeof(message_buffer),1);
 
    message_length = sizeof(message_buffer);
  }
 /* On subsequent calls, locate addressability to the program's local  */
 /* variables via the pointer saved immediately before the answer_area */
 
 else
  { answer_buffer_ptr = ( answer_buffer * ) (((long) (answer_area)) - 4);
  }
                      .
                      .
                      .

On the first call, the sample code allocates dynamic storage for:

The pointer to the program variable buffer is saved at a fixed location (a known offset), in the first n bytes of the buffer, for the answer set description or row (called the answer buffer). This is illustrated in the image below.

For example, you might allocate an answer buffer of 1,004 bytes with 1,000 bytes used to store the largest answer set description and the 4 extra bytes used to store the pointer to the program variable buffer.

As shown in the following figure, the pointer stored in the control block's answer_area points to the answer buffer, excluding the 4 bytes used to store the pointer to the program variable buffer. That is, the pointer is directed toward the beginning of an answer set description or row. (The message_area could also be used to store the pointer to the program variable buffer, but for the purpose of illustration, the answer_area was chosen.)

The length of bytes to be stored in the control block's answer_length would be 1,004 minus 4, or a value of 1,000, to reflect the value of the largest answer set description or row.

To determine the address of the program variable buffer on subsequent calls, the program would subtract the size of the pointer to the program variable buffer (4 bytes on most machines) from the answer_area in the control block.

When freeing memory on exit, the program determines the size of the answer buffer by adding the answer_length to the size of the pointer to the program variable buffer.

When using this technique, it is important to keep the answer_area in the control block consistent with the definition in the interface. Always point the answer_area and message_area to valid data when control is returned to the server. Program variables are kept in any allocated memory buffer using this technique.

The program must free all memory allocated during execution before returning an action_value of 9 (exit) to the server. This requirement applies to the memory for program variables, messages, answer set descriptions, and rows. If all memory is not freed at program exit, server failure may result at a later time.


Top of page

Example: Storing Program Values in COBOL

In COBOL, one way to save program variables across invocations of a program is to allocate one block of storage big enough to hold:

Dynamic storage is acquired in this example using EXEC CICS GETMAIN in COBOL under CICS as the reference platform, but any language supporting the setting of dynamic storage can be used with the syntax specific to that language.

The following sample COBOL code describes a MESSAGEAREA. It provides the field MESSAGE-OUT for messages, answer set descriptions (CREATE TABLEs), and rows. It provides the fields NUM-ROWS, LAST-REC, and CLOSE-PENDING-FLAG for program values to be retrieved in subsequent invocations.

01  MESSAGEAREA.
    05  MESSAGE-OUT              PIC X(1000).
    05  NUM-ROWS                 PIC S9(8)  COMP-4.
    05  LAST-REC                 PIC S9(8)  COMP-4.
    05  CLOSE-PENDING-FLAG       PIC X.
        88  CLOSE-PENDING        VALUE "1".
        88  CLOSE-NOT-PENDING    VALUE "0".
    05  FILLER                   PIC X(15).

The code to store values is:

      IF FLAG-FIRST-TIME
        MOVE LENGTH OF MESSAGEAREA TO MESSAGE-LENGTH
******* GETMAIN, SET LENGTH, ADDRESSES
        EXEC CICS GETMAIN SET (ADDRESS OF MESSAGEAREA)
                  FLENGTH (MESSAGE-LENGTH)
                  INITIMG (INITVALUE)
        END-EXEC
        SET MESSAGE-ADDRESS TO ADDRESS OF MESSAGEAREA
        SET ANSWER-ADDRESS TO ADDRESS OF MESSAGEAREA
      ELSE
***** IF NOT THE FIRST TIME, RETRIEVE THE GETMAIN ADDRESS
***** FROM EITHER COMMAREA ADDRESS, AND SET THE ADDRESS
***** OF THE GETMAIN AREA SO IT IS ADDRESSABLE IN COBOL.
        SET ADDRESS OF MESSAGEAREA TO MESSAGE-ADDRESS.

The previous code fragment is executed each time the program is invoked. The first time, the program uses EXEC CICS GETMAIN to allocate the storage to the length of the MESSAGEAREA. On each subsequent execution, it gets the address of the MESSAGEAREA from the field MESSAGE-ADDRESS.

The following figure illustrates the program logic in the code fragment. In the figure, the field MESSAGE-ADDRESS in the code is represented as message_area in the control block.

In this example, the program allocates a buffer (MESSAGEAREA) of 1,000 bytes (for the largest message, answer set description, or row to be returned), plus 24 bytes for the program variables.

In the control block:

To address program variables stored between invocations in this way, use

SET ADDRESS OF MESSAGEAREA TO MESSAGE-ADDRESS

as shown in the preceding sample code. This code enables the program to refer to the variables NUM-ROWS, LAST-REC, and CLOSE-PENDING-FLAG.

To free storage allocated this way, use:

EXEC CICS FREEMAIN (MESSAGEAREA) END-EXEC

CICS frees the correct length.

Below is output from a sample session that runs CPGCICS using RDAAPP, a test program supplied on your distribution media.

<<< RDAAPP : Initializing API SQL, Version x   >>>
<<< Initialization Successful >>>
Trace level ?
 
Enter User Name :
 
Enter Password :
 
Enter Server name (Hit return for 'CICS    ') :
 
<<< Successfully connected to server >>>
Enter (S/P <sql stmt;> / X <RPC> <parms> / D <tbl> / E <prep id> / C/R / 
Q) :
x cpgcics 1
Please Wait.
000100
S. D. BORMAN
SURREY, ENGLAND
3215677826
11 81
$0100.11
*********
<<< 1 record(s) processed. >>>
Enter (S/P <sql stmt;> / X <RPC> <parms> / D <tbl> / E <prep id> / C/R / 
Q) :
***

Top of page

Example: Storing Program Values in COBOL II

The following example uses VTAM MVS COBOL II as the reference platform.

To allocate dynamic storage, use the 'GETCOR' function, supplied on your distribution media in the module CPGUSRO.

Specify the following three parameters on the function call:

The following is the code for allocating dynamic storage:

01  COR-DATA.
    05  MESSAGEAREA-LENGTH               PIC S9(8) BINARY.
    05  MESSAGEAREA-ADDRESS              POINTER.
    05  COR-RESP                         PIC S9(8) BINARY.
                    .
                    .
                    .
MOVE LENGTH OF MESSAGEAREA TO MESSAGEAREA-LENGTH
CALL 'GETCOR' USING
BY REFERENCE MESSAGEAREA-LENGTH,
    BY REFERENCE MESSAGEAREA-ADDRESS,
    BY REFERENCE COR-RESP

To free dynamic storage on program exit, use the 'FRECOR' function, also supplied on your distribution media.

Specify the following three parameters on the function call:

The following is the code for freeing dynamic storage:

CALL 'FRECOR' USING
    BY CONTENT LENGTH OF MESSAGEAREA,
    BY REFERENCE MESSAGEAREA,
    BY REFERENCE COR-RESP

Note: Use the COR-RESP return code, not the COBOL RETURN-CODE, as the latter has an arbitrary value.

To link edit the sample program (supplied as CPGVTAM on your distribution media), use the statements below:

INCLUDE EDALIB(CPGUSRO)
  INCLUDE OBJECT
  MODE AMODE(31),RMODE(ANY)
  ENTRY CPGVTAM
  NAME CPGVTAM(R)

CPGUSRO is a non-executable module that provides dynamic linkage to 'GETCOR' and 'FRECOR'.


Top of page

Example: Linking Program Variables to the Control Block

The following code fragment illustrates how to link program variables to the answer and message pointers, defined in the control block in Control Block Specification.

WORKING-STORAGE SECTION.
01  MESSAGE-BUFFER            PIC X(100) VALUE SPACES.
01  ANSWER-BUFFER             PIC X(100) VALUE SPACES.
   .
   .
   .
SET ANSWER-ADDRESS TO ADDRESS OF ANSWER-BUFFER
SET MESSAGE-ADDRESS TO ADDRESS OF MESSAGE-BUFFER

Note: OpenVMS uses the keywords "TO REFERENCE OF" instead of "TO ADDRESS OF".


Top of page

Example: Checking for First-time Execution

The following code checks for the initial execution of the program so that it initializes program variables on the first call:

PROCEDURE DIVISION USING CPGUB.
A010-BEGIN.
     IF FLAG-FIRST-TIME
        PERFORM A020-INIT-DATA
     ELSE
        IF PARM-COUNT < 5 AND PARM-REMAIN > ZERO PERFORM A030-READ-DATA.
     EXIT PROGRAM.

Top of page

Example: Allocating and Freeing Dynamic Storage

The following code illustrates how to allocate and free dynamic storage used for storing program values:

01 NUMBER-OF-BYTES PIC S9(9) COMP.
01 BASE-ADDRESS    PIC S9(9) COMP.
01 RET-STATUS      PIC S9(9) COMP.
      .
      .
      .
A080_ALLOC_STORAGE.
   MOVE +1000 TO NUMBER-OF-BYTES.
   CALL "LIB$GET_VM"
      USING BY REFERENCE NUMBER-OF-BYTES, BASE-ADDRESS
      GIVING RET-STATUS.
      .
      .
      .
A090_FREE_STORAGE.
   MOVE +1000 TO NUMBER-OF-BYTES.
   CALL "LIB$FREE_VM"
      USING BY REFERENCE NUMBER-OF-BYTES, BASE-ADDRESS
      GIVING RET-STATUS.

iWay Software