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:
Messages returned by the program are pointed to by the control block field message_area. The length is given in the field message_length.
Answer set descriptions or rows (see below) returned by the program are pointed to by the control block field answer_area. The length is given in the field answer_length.
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:
|
RPG | (IBMÂ i only)
|
The portable COBOL examples have specifically been tested with IBM Enterprise COBOL V3R2, HP OpenVMS COBOLv2.7, and 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.
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.
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) : ***
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'.
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".
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.
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 |