Showing posts with label COBOL. Show all posts
Showing posts with label COBOL. Show all posts

Monday, July 20, 2026

Why Binary Fields Never Cause an S0C7 Abend, but Zoned Decimal and Packed Decimal Can

In Mainframe, usually numeric calculations are performed using Binary, Zoned-decimal and packed decimals

One interesting fact is that binary fields never cause an S0C7 data exception, whereas zoned decimal and packed decimal fields can. Let's explore why.
 
Binary fields will NEVER cause S0C7 abend
 
This is demonstrated using the below COBOL code.
 
WORKING-STORAGE SECTION.                            
01 JUNK                            PIC X(04).       
01 WS-BINARY        REDEFINES JUNK PIC 9(08) COMP.  
 
PROCEDURE DIVISION.                                 
 
   MOVE 'ABCD'      TO JUNK.                         
   DISPLAY  'WS-BINARY: ' JUNK WS-BINARY            
   ADD 1 TO WS-BINARY                               
   DISPLAY  'WS-BINARY: ' JUNK WS-BINARY            
                                                    
   MOVE '#$%='      TO JUNK.                        
   DISPLAY  'WS-BINARY: ' JUNK WS-BINARY            
   ADD 1 TO WS-BINARY                               
   DISPLAY  'WS-BINARY: ' JUNK WS-BINARY            
 
The above code does NOT produce S0C7. Output of the above code snippet as follows
 
WS-BINARY: 50766788  
WS-BINARY: 50766789  
WS-BINARY: 69589118
WS-BINARY: 69589119  
 
Case 1: Value "ABCD"
 
'ABCD' is indirectly moved to WS-BINARY. The hexadecimal equivalent of 'ABCD' is X’C1C2C3C4’.  If you put this HEX decimal value in calculator, you will get 3,250,766,788. Since picture clause of WS-BINARY is 9(8), the last 8 digit 50,766,788 is displayed in the first DISPLAY statement.
 
When you add 1 to this value(3,250,766,788), it become 3,250,766,789 and second DISPLAY statement displays last 8 digits which is 50,766,789.
 
 
Case 2: Value "#$%="
 
'#$%=' is indirectly moved to WS-BINARY. The hexadecimal equivalent of '#$%='  is X’7B5B6C7E’.  If you put this HEX decimal value in calculator, you will get 2,069,589,118. Since picture clause of  WS-BINARY is 9(8), the last 8 digit 69,589,118 is displayed in the third DISPLAY statement.
 
When you add 1 to this value(2,069,589,118), it become 2,069,589,119 and fourth DISPLAY statement displays last 8 digits which is  69,589,119.
 
 
Understanding Zoned Decimal and Packed Decimal
 
Before discussing how an S0C7 Data Exception occurs with decimal data, it is important to understand how zoned decimal and packed decimal values are stored internally.
 
Typical zoned decimal declarations in COBOL are shown below:

 
01 WS-NUMBER1 PIC 9(5).     -> Unsigned zoned decimal
01 WS-NUMBER1 PIC 9(5)V99.  -> Unsigned zoned decimal
01 WS-NUMBER2 PIC S9(5)V99. -> Signed zoned decimal
 
Internal representation of Zoned Decimal in Memory
 
In a zoned decimal field:
  • Each decimal digit occupies one byte.
  • Each byte has 2 nibbles, and each nibble has 4 bits.
  • The high-order 4 bits (zone portion) of each byte will always have b’1111’ (hex F) except the last byte.
  • The low-order 4 bits (decimal portion) contain the actual numeric digit (0–9).
  • In the last byte, first 4 bits will store the sign and second 4 bits will store the last numeric digit.
 

Value

Internal Hex Representation

Unsigned 12345

X'F1F2F3F4F5'

+12345

X'F1F2F3F4A5'

X'F1F2F3F4C5'

X'F1F2F3F4E5'

-12345

X'F1F2F3F4B5'

X'F1F2F3F4D5'

 
 
Sign is stored in the zone portion (high-order 4 bits) of the last byte. Valid signs are given below
 

Sign

Zone Nibble

Unsigned

F

Positive

A,C,E

Negative

B,D

 
Positive signs A, E and negative signs “B” are called non-preferred signs.
 
System always uses “C” for positive signs and “D” for negative signs, but still accepts A,B,E signs
 
When fields with signs A/B/E are involved in the calculations, ultimately sign will be converted to either “C” for positive and “D” for negative
 
Demonstrating Zoned Decimal Sign Handling
 
Let us see how different sign behaves for a zoned decimal field using the below code snippet.
 
WORKING-STORAGE SECTION.          
01 WS-9                PIC S9(05).               
01 WS-X REDEFINES WS-9 PIC X(05). 
 
PROCEDURE DIVISION.               
PARA1.                            
     MOVE X'F1F2F3F4F5' TO WS-X   
     PERFORM CHECK-SIGN           
     MOVE X'F1F2F3F4A5' TO WS-X   
     PERFORM CHECK-SIGN           
     MOVE X'F1F2F3F4C5' TO WS-X   
     PERFORM CHECK-SIGN           
     MOVE X'F1F2F3F4E5' TO WS-X   
     PERFORM CHECK-SIGN           
                                  
     MOVE X'F1F2F3F4B5' TO WS-X   
     PERFORM CHECK-SIGN           
     MOVE X'F1F2F3F4D5' TO WS-X   
     PERFORM CHECK-SIGN           
     GOBACK.   
                
CHECK-SIGN.                       
     IF WS-9 > 0                  
        DISPLAY WS-9 ' POSITIVE'  
     ELSE                         
         IF WS-9 = 0                
            DISPLAY WS-9 ' ZEROS'   
         ELSE                       
            DISPLAY WS-9 ' NEGATIVE'
         END-IF                     
      END-IF.                       
      ADD 1              TO WS-9    
      DISPLAY 'WS-X : ' WS-X.       
 
 
 

Output of the program is given below

Hex value for the number displayed

12345 POSITIVE  

X'F1F2F3F4F5'

WS-X : 1234F   

X'F1F2F3F4C6'

1234v POSITIVE 

X'F1F2F3F4A5'

WS-X : 1234F   

X'F1F2F3F4C6'

1234E POSITIVE 

X'F1F2F3F4C5'

WS-X : 1234F   

X'F1F2F3F4C6'

1234V POSITIVE 

X'F1F2F3F4E5'

WS-X : 1234F   

X'F1F2F3F4C6'

1234§ NEGATIVE 

X'F1F2F3F4B5'

WS-X : 1234M   

X'F1F2F3F4D4'

1234N NEGATIVE 

X'F1F2F3F4D5'

WS-X : 1234M   

X'F1F2F3F4D4'


Typical packed decimal declarations in COBOL are shown below:
 
01 WS-NUMBER1 PIC 9(5) COMP-3.     -> Unsigned packed decimal

01 WS-NUMBER1 PIC 9(5)V99 COMP-3.  -> Unsigned packed decimal
01 WS-NUMBER2 PIC S9(5)V99 COMP-3. -> Signed packed decimal

Internal representation of packed Decimal in Memory
 
In a packed decimal field:
  • Each byte has two decimal digits except the last byte
  • In the last byte, first 4 bits will store the numeric digit and second 4 bits will store the sign.
  • If an odd number of digits exists, the unused high-order nibble is padded with zero.
 

Value

Internal Hex Representation

Unsigned 12345

X'12345F'

+12345

X'12345A'

X'12345C’

X'12345E’

-12345

X'12345B'

X'12345D’

+123456

X’0123456C’

 
Similar to zoned decimal, system always uses “C” for positive signs and “D” for negative signs for packed decimals, but still accepts A,B,E signs
 
Moving Invalid Data into a Zoned Decimal Field
 
Let us know test moving junk values to a zoned decimal.
 
WORKING-STORAGE SECTION.                           
01 JUNK                            PIC X(04).      
01 WS-ZONED-DECIMAL REDEFINES JUNK PIC 9(04).    
 
PROCEDURE DIVISION.                                
                                                   
   MOVE 'ABCD'      TO JUNK.                       
   DISPLAY  'WS-ZONED-DECIMAL: ' WS-ZONED-DECIMAL  
   ADD 1 TO WS-ZONED-DECIMAL                       
   DISPLAY  'WS-ZONED-DECIMAL: ' WS-ZONED-DECIMAL  
                                                   
   MOVE X'1A1B1C1D' TO JUNK.                       
   DISPLAY  'WS-ZONED-DECIMAL: ' WS-ZONED-DECIMAL  
   ADD 1 TO WS-ZONED-DECIMAL                       
   DISPLAY  'WS-ZONED-DECIMAL: ' WS-ZONED-DECIMAL  
   GOBACK.                                         
 
Output of the above code snippet as follows:

 
WS-ZONED-DECIMAL: ABCD 
WS-ZONED-DECIMAL: 1235 
WS-ZONED-DECIMAL: X'1A1B1C1D' 
CEE3207S The system detected a data exception (System Completion Code=0C7).
 
Code Explanation

Mainframe does not have any instruction to perform arithmetic operations on zoned decimals. So, it first converts zoned-decimals to packed-decimal and then performs arithmetic operations on the packed-decimal.
 
The first move indirectly populated ‘ABCD’(hex value X’C1C2C3C4’) to WS-ZONED-DECIMAL.
 
Before adding one to WS-ZONED-DECIMAL, Value X’C1C2C3C4’ need to be converted to packed-decimal.
 
How Zoned Decimal to Packed Decimal Conversion Works
 
During the conversion process:
  • The digit portion of each zoned-decimal byte is treated as a numeric digit.
  • The zone bits are ignored except in the rightmost byte.
  • The zone bits of the last byte become the sign nibble.
  • Digits are packed together from left to right.
  • If the packed-decimal result contains an unfilled nibble, it is padded with zero on the left. 
No validation occurs during the conversion itself.
 
Example 1: ‘ABCD’ - X'C1C2C3C4'
 
Value X’C1C2C3C4’ becomes X‘01234C’ after zoned-decimal to packed-decimal conversion.
 
Therefore, the second DISPLAY statement shows 1235, because the temporary packed-decimal representation (X'01234C') was successfully incremented by one during the arithmetic operation.
 
Example 2: X'1A1B1C1D'
 
Value X'1A1B1C1D' becomes X‘0ABCD1’ after zoned-decimal to packed-decimal conversion.
 
Since X‘0ABCD1’ does not adhere to packed-decimal representation, when we tried to add one to it, system produced S0C7 abend


When Does a Packed-Decimal (COMP-3) Field Cause an S0C7 Abend?

As discussed earlier, a packed-decimal field must conform to the valid packed-decimal format. If the field contains invalid digits or an invalid sign nibble and an arithmetic operation is attempted on it, the processor detects the invalid data and raises an S0C7 (Data Exception) abend.

Wednesday, July 15, 2026

Ignoring COBOL File Status Checks Led to Silent Data Loss

 At first glance, it looked like a straightforward COBOL batch program.

It read a sequential file from beginning to end. For each record, it performed a random lookup in a KSDS file. If a matching record existed, it updated the record. If no match was found, it inserted a new record into the KSDS file.

Hidden deep within the program was a critical flaw: after every write operation, the program never checked the file status code to verify whether the write had succeeded.

Everything worked perfectly—until the KSDS file reached its maximum size limit of 4 GB.

Once that limit was reached, every attempt to add a new record failed. The system dutifully generated the message:

IEC070I 034(004)-220

However, because the program never validated the write status, it continued processing records as if every insert had been successful.  

The result?

New records were silently discarded while the batch job completed normally, giving everyone the illusion that everything was working as intended.

The issue remained undetected for months, quietly preventing new data from being added to the KSDS file until someone finally traced the missing records back to the unnoticed write failures.

Key Takeaway : Never assume a file operation succeeds.

After every file read, write, rewrite, delete, or open operation:

Check the file status code.
Handle error conditions appropriately.
Log and escalate failures when necessary.

Wednesday, February 18, 2026

COBOL Binary search

We had a situation where we loaded a large VSAM file into memory to be searched by online CICS application. We encountered problems trying to emulate the VSAM START verb using COBOL SEARCH ALL verb when the key does not exist in the array. To get around this we had to write our own BINARY SEARCH in COBOL. 

The array which we are searching contains about 240,000 entries. 

We found that the hand coded BINARY SEARCH was significantly more efficient in terms of CPU usage than the COBOL "SEARCH ALL" verb. 

The code was modeled after KNUTH's binary search, see this link 

http://www.z390.org/contest/p21/P21DW1.TXT 

the key to maximizing the performance of the code below was to use PIC S9(8) COMP for all the binary fields. This avoids the check for binary overflow in the generated code. Also we used the compiler option TRUNC(OPT) to avoid the code generated to check for truncation. 

Note --> the IF statement after the PERFORM loop is to check for a "not found" condition. To emulate the VSAM START command we need to position the INDEX to the first row which is >= the search key. 


           MOVE +1                     TO BIN-LOW. 
           MOVE BCNTR-TCA1-ENTRIES-USED 
                                       TO BIN-HIGH. 

           PERFORM WITH TEST AFTER 
               UNTIL BIN-LOW > BIN-HIGH 
               COMPUTE BIN-RANGE = BIN-HIGH - BIN-LOW 
               COMPUTE BIN-MID = (BIN-RANGE / 2) + BIN-LOW 
               SET TCA1-INDEX          TO BIN-MID 
               EVALUATE TRUE 
                   WHEN TCA1-KEY (TCA1-INDEX) = SRCH-TCA1-KEY 
                       MOVE 1          TO BIN-RANGE 
                       COMPUTE BIN-LOW = BIN-HIGH + 1 
                   WHEN TCA1-KEY (TCA1-INDEX) < SRCH-TCA1-KEY 
                       COMPUTE BIN-LOW  = BIN-MID + 1 
                   WHEN OTHER 
                       COMPUTE BIN-HIGH  = BIN-MID - 1 
               END-EVALUATE 
           END-PERFORM. 


           IF TCA1-KEY (TCA1-INDEX) < SRCH-TCA1-KEY 
               SET TCA1-INDEX          UP BY +1 
           END-IF. 

Saturday, June 8, 2024

Data overlay caused by COBOL reference modification

If the length of the target field in a MOVE statement is greater than or equal to the length of the source field, and the source field has reference modification, and the length of the source field is provided through a variable, and the length of the reference modification is greater than the length of the target field, it will result in data overlay in the subsequent fields of the target field.

By supplying a value of 15 in the SYSIN card for the length field in the program below, we can observe the occurrence of data overlay. 

        IDENTIFICATION DIVISION.               
        PROGRAM-ID. HELLO.                     
        DATA DIVISION.                         
        WORKING-STORAGE SECTION.               
        01 GRP-A.                              
           05 WS-X   PIC X(05) VALUE '12345'.  
           05 FILLER PIC X(05) VALUE 'ABCDE'.  
           05 FILLER PIC X(05) VALUE 'FGHIJ'.  
        01 GRP-B.                              
           05 WS-A    PIC X(05).               
           05 WS-B    PIC X(10).               
        01 WS-LEN     PIC 9(02).               
        PROCEDURE DIVISION.                    
           MOVE SPACES TO WS-B                 
           DISPLAY 'WS-B: ' WS-B.              
           ACCEPT WS-LEN 
           MOVE WS-X (1:WS-LEN) TO WS-A        
           DISPLAY 'WS-B: ' WS-B. 
           STOP RUN.    

Output of the above program

 WS-B:               
 WS-B: ABCDEFGHIJ    

This issue is observed in IBM Enterprise COBOL for z/OS  6.3.0. 

If the length of the target field is less than the length of the source field, then this iissue wont occur


Monday, January 8, 2024

Difference between EXIT PROGRAM, GOBACK, STOP RUN in COBOL

Let us assume we have a Batch JOB step which executes MAIN-PGM. 
Let us assume job step has below call chain. 

MAIN-PGM --> SUB-PGM1 --> SUB-PGM2 --> SUB-PGM3

That is MAIN-PGM calls SUB-PGM1
SUB-PGM1 program inturn calls SUB-PGM2
SUB-PGM2 program inturn calls SUB-PGM3

A run unit is a running set of one or more programs that communicate with each other by COBOL static or dynamic CALL statements. So, the above entire flow is called "run unit"

EXIT PROGRAM or GOBACK in sub programs(called programs) returns the control to the calling program.

For example, if we code EXIT PROGRAM or GOBACK in SUB-PGM3, then control returns to the point in the SUB-PGM2 immediately following the CALL statement that invoked SUB-PGM3

GOBACK in main program MAIN-PGM passes the control to the operating system program that invoked it.

EXIT PROGRAM in main program does NOT do anything, control goes to the next statement that immediately follows EXIT PROGRAM.
So, EXIT PROGRAM works only in sub programs (called programs).

If we code STOP RUN in SUB-PGM3 (or any other program in the calling chain), it will directly pass the control to the operating system program that invoked main program MAIN-PGM. That is STOP RUN ends the execution of entire "run unit".

Tuesday, October 26, 2021

How to pass data to a program present many levels down the calling chain

Let us say we have below calling chain

Program A --> Program B ---> Program C ---> Program D --> Program E

And we want to pass data from "Program B" to "Program E"

Common approaches for passing data between programs include:
  • Passing data through the LINKAGE section from Program B → Program C → Program D → Program E
  • Storing the data in a VSAM file
  • Storing the data in a DB2 table
  • Using CICS TSQ/TDQ in online environments
  • Channels/Containers in CICS

While these methods are widely used and appropriate in many scenarios, they may require changes to multiple programs, additional I/O operations. For example, passing data through the LINKAGE section requires every intermediate program in the call chain to receive and forward the data, even when those programs have no need for it. Similarly, storing data in VSAM, DB2, or CICS queues introduces persistence and access overhead that may be unnecessary for small amounts of transient data.

Channels and Containers work only in CICS programs. They cannot be used in batch jobs, so they are not suitable if the solution must support both online and batch processing.

In situations where a small piece of data needs to be shared between non-adjacent COBOL programs during the same execution path, a lightweight cache program can provide a simple alternative. This approach avoids modifications to intermediate programs while keeping the data readily accessible to programs located several levels down the calling chain.

This technique works well in both online and batch environments.

Here I am using a sub program called "CACHEPGM" as a cache. 

       IDENTIFICATION DIVISION.  
       PROGRAM-ID.    CACHEPGM.
       ENVIRONMENT DIVISION. 
       DATA DIVISION.       
       WORKING-STORAGE SECTION.  
       01 WS-SAVE-DATA PIC X(80) VALUE SPACES. 
       LINKAGE SECTION.                        
       01 LS-PARAMETERS.  
          05 LS-FUNC          PIC X(4) VALUE SPACES. 
               88 LS-SET-DATA           VALUE 'SET'.  
               88 LS-GET-DATA           VALUE 'GET'.      
           05 LS-DATA         PIC X(80). 
       PROCEDURE DIVISION USING LS-PARAMETERS. 
           EVALUATE TRUE  
             WHEN LS-SET-DATA  
               MOVE LS-DATA      TO WS-SAVE-DATA   
             WHEN LS-GET-DATA 
               MOVE WS-SAVE-DATA TO LS-DATA  
           END-EVALUATE   
           GOBACK.    

From Program B, call CACHEPGM with the SET function and the data that needs to be shared. CACHEPGM stores the data in its Working-Storage area for later use.

From Program E, call CACHEPGM with the GET function to retrieve the previously stored data. CACHEPGM returns the value from its Working-Storage area to the calling program.

Sunday, January 22, 2017

VSAM file handling

This tutorial explains how file operations are performed on ESDS, KSDS, RRDS files of VSAM access method.

This tutorial is organized by the following topics.

  • VSAM File Organization  
  • File Access Modes  
  • In the ENVIRONMENT Division
  • In the PROCEDURE Division
  1. ·  The OPEN Statement
  2. ·  The READ Statement
  3. ·  The START Statement
  4. ·  The WRITE Statement
  5. ·  The REWRITE Statement
  6. ·  The DELETE Statement
  7. ·  The CLOSE Statement  


VSAM File Organisation

There are three types of file organisation you can use with VSAM.

VSAM sequential file organisation -

Also referred to as VSAM ESDS (Entry Sequenced Data Set) organisation. In such a data set the records are stored in the order in which they were entered. The order of the records is fixed. Records in sequential files can only be accessed (read or written) sequentially.

VSAM indexed file organisation –

Also referred to as VSAM KSDS (Key Sequence Data Set) organisation. In a KSDS the records are ordered according to the collating sequence of a prime key field defined in each record. This key field consists of one or more consecutive characters within the records and uniquely identifies each record. A prime key for a record might be, for example, an employee number or an invoice number. In your COBOL program, you specify this key through the clause:

RECORD KEY IS data-name

where data-name is the name of the key field as you defined it in the record description entry in the Data Division. The data set can be accessed sequentially, i.e. in primary key sequence, or directly by specifying the key of the record required.

You can also specify one or more alternate keys to use for retrieving records. Using alternate keys you can access the file to read records in some sequence other than the prime key sequence. For example, you could access the file through employee department rather than through employee number. Alternate keys need not be unique, more than one record will be accessed, given a department number as a key. This is permitted if alternate keys are specified as allowing duplicates
.
VSAM relative-record file organisation -

Also referred to as VSAM RRDS (Relative-Record Data Set) organisation. A RRDS is a series of fixed length slots in storage into which you place records. The relative key identifies the record - the relative key being the relative record number of the slot that the record occupies.

File Access Modes
You can access records in VSAM KSDS and RRDS files in three ways, sequentially, randomly or dynamically. (Records in an ESDS file can be accessed sequentially).

1. Sequential Access -
For indexed files, records are accessed in the order of the key field selected (either primary or
alternate). For relative files, records are accessed in the order of the relative record numbers.

2. Random Access -
For indexed files, records are accessed according to the value that you place in the key field.
For relative files, records are accessed according to the value that you place in the relative key.

3. Dynamic Access
Dynamic access is a mixture of sequential and random access within the same program. Using
dynamic access, you can write one program to perform both sequential and random processing,
accessing some records in sequential orders and others by their keys. As this method allows the
greatest flexibility it is usually coded.

Example
Suppose you had a KSDS of employee records and the hourly wage formed the record key. Also
suppose your program was interested in those employees earning £7 and £9 per hour and those
earning £15 per hour and above.

To do this, retrieve the first record randomly based on the key of 0700. Then begin reading
sequentially until the wage field exceeds 0990. Then switch back to random read, this time based
on a key of 1500 and then switch back to reading sequentially until you reach the end of the file.


In the ENVIRONMENT Division

For an ESDS:

SELECT file-name
ASSIGN TO UT-AS-ddname
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE-STATUS IS data-name-1 data-name-2.

Notes:
(1) The ORGANIZATION and ACCESS clauses are optional as the default is SEQUENTIAL.

(2) The PASSWORD clause is not used within Boots.

(3) The FILE STATUS clause monitors the execution of each input-output request for the file. This clause is optional but when it is specified, the system moves a value into data-name-1 after each input-output request that explicitly or implicitly refers to this file. This field is defined in the Data Division (although not on the File Section) as a two-byte alphanumeric item and must not be the object of an OCCURS DEPENDING ON clause.

(4) Data-name-2 can be coded in VS COBOL II programs to access native VSAM return code
information to the COBOL program. This field must be defined as a group item of 6 bytes in the
Working-Storage Section or Linkage Section of the Data Division.

The first 2 bytes of data-name-2 contain the VSAM return code in binary notation.
It can take a value of 0, 8, or 12.
The next 2 bytes contain the VSAM function code in binary notation. The value for this
code is defined as 1, 2, 3, 4, or 5.
The last 2 bytes of data-name-2 contain the VSAM feedback code again in binary. The
code value is 0 through to 255.

The Function Code and Feedback Code are set only if the Return Code is set to non-zero. If they
are set when the Return Code is zero, the contents of the fields are not reliable.

Example

SELECT VSAM-MASTER ASSIGN TO UT-AS-VSMAST
FILE STATUS IS STATUS-CODE VSAM-RETURN-CODE.

WORKING-STORAGE SECTION.
01 STATUS-CODE PIC XX.
01 VSAM-RETURN-CODE.
05 VSAM-R15-RETURN-CODE PIC S99 COMP.
05 VSAM-FUNCTION-CODE PIC S9 COMP.
05 VSAM-FEEDBACK-CODE PIC S999 COMP.

In the ENVIRONMENT Division

For a KSDS:

SELECT file-name
ASSIGN TO UT-ddname
ORGANIZATION IS INDEXED
ACCESS MODE IS SEQUENTIAL / RANDOM / DYNAMIC
RECORD KEY IS data-name-1
ALTERNATIVE RECORD KEY IS data-name-2
WITH DUPLICATES
FILE-STATUS IS data-name-3 data-name-4
Notes:
(1) The RECORD KEY clause specifies the data item within the record that is the prime record key for the indexed file. The value contained in this data item must be unique among records in the file. Data-name-1 must be described as an alphanumeric item within the record description. An IBM extension allows data-name-1 to be defined to be numeric, numeric-edited, alphanumeric-edited or alphabetical though the key is still treated as an alphanumeric item for the input and output statements for the named file.

(2) The same rules apply to the ALTERNATE RECORD KEY clause as in note 1. Data-name-2
must not be at the same position on the record as the primary index key or any other alternate
record key field.

(3) If the DUPLICATE clause is specified, the values contained in the ALTERNATE RECORD
KEY data item may be duplicated within any records in the file. In sequential access, the records
with duplicate keys are retrieved in the order in which they were placed in the file. In random
access, only the first record written of a series of records with duplicate keys can be retrieved.

Example
SELECT MASTER-FILE ASSIGN TO UT-KSDSMAST
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS MAT-EMP-NO
ALTERNATE RECORD KEY IS MAST-EMP-DEPT
WITH DUPLICATES
FILE STATUS IS STATE-CODE VSAM-RETURN-CODE.


In the ENVIRONMENT Division

For RRDS:

SELECT file-name
ASSIGN TO UT-ddname
ORGANIZATION IS RELATIVE
ACCESS MODE IS SEQUENTIAL / RANDOM / DYNAMIC
RELATIVE KEY IS data-name-1
FILE-STATUS IS data-name-2 data-name-3.
Notes :

(1) The RELATIVE KEY clause identifies a data-name that specifies the relative record number for a specific logical record within the relative file. Data-name-1 must be defined as an unsigned integer data item and must not be included in the record description entry for the data set. That is the relative key is not part of the record.

(2) When ACCESS MODE IS SEQUENTIAL the Relative Key data field need not be specified
unless the START statement (see page 10) is to be used. When the START statement is used the
contents of data-name-1 are used to determine where processing of the data set is to begin. If a
value is put into the Relative Key field, and a START statement is not used, then the value is ignored and processing begins with the first record in the file.

(3) For RANDOM or DYNAMIC access the Relative Key field must be specified and a value
inserted as it is used to obtain the record with that relative record number.


In the Procedure Division

1. The OPEN Statement:
OPEN INPUT / OUTPUT / I-O / EXTEND file-name-1 file-name-2 ...

Notes:

(1) INPUT permits opening the file for input operations.

(2) OUTPUT permits opening the file for output operations. This is specified when a file is being
created. If OUTPUT is specified for a file that contains records, the file will be replaced by the new data.

(3) I-O permits opening the file for input and output operations. The I-O option may be specified
only for the files assigned to direct access devices.

(4) EXTEND permits opening the file for output operations. The EXTEND phrase is only allowed for sequential access files. When an OPEN EXTEND statement is executed, the file is prepared for the addition of records immediately following the last record in the file. (The record with the highest prime record key value is considered the last record.) Subsequent WRITE statements add records as if the file were opened OUTPUT.

(5) After the OPEN statement you should check the "Status Code" for successful completion of the OPEN. If it has successfully executed then the Current Record Pointer is set to the first record in the data set. In the case of KSDS this will be the record with the lowest primary index key and for a RRDS it is set to 1. If the data set is empty then the Current Record Pointer will be set to indicate end of file in the case of a sequential read being executed.


2. The READ Statement

For a sequential READ

READ file-name NEXT RECORD
INTO identifier-1
AT END statement-1
END-READ

Notes:

(1) The sequential READ must be used for all data sets in sequential access mode.

(2) The NEXT RECORD is the next in the logical sequence of records. For a KSDS this is the
ascending value of the current Key of Reference (i.e. the primary index key or the alternate index
key being used). For RRDS the sequence is the ascending value of the Relative Record numbers
for the records that exist in the data set. The word NEXT can be omitted for files that have
sequential access mode.

(3) Before the READ statement is executed, the Current Record Pointer must be set by a successful OPEN, START, or READ statement.

(4) If the Current Record Pointer indicates that no next logical record exists, the following occur in the order specified:

(a) The value '10' is placed into the "Status-Code" associated with the file-name to indicate the AT END condition.
(b) If the AT END clause has been specified control is transferred to statement-1 in the
clause.

(5) If the FILE STATUS clause has been used for the data set then the "Status-Code" field is
updated when the READ statement has been executed.


For a Direct READ:

READ file-name RECORD
INTO identifier-1
KEY IS data-name-1
INVALID KEY statement-1
END-READ

Notes:
(1) Direct retrieval is used for KSDS and RRDS with ACCESS MODE specified as RANDOM or DYNAMIC. As stated previously, DYNAMIC access allows both direct access and sequential
access to the data set and because of this greater flexibility this is used at Boots.

(2) The KEY IS clause is specified only for a KSDS. Data-name-1 must identify a record key
associated with file-name - either the prime record key or any alternate record key.

(3) The value of the key of the KSDS record required must be moved to the key field before the
READ statement is issued. Then when the READ statement is executed it causes this value to be
compared with the value of the corresponding key data item in the file records, until the first record having an equal value is found. The Current Record Pointer is then positioned to this record, which is then made available. If no match is found then the INVALID KEY condition exists. In this case a value of '23' is returned to the "Status CODE" field and control passes to statement-1.

(4) If the KEY IS phrase is not specified, the prime RECORD KEY or the last key field that was
used by a READ becomes the key of reference for this request.

(5) For the RRDS, execution of the direct READ statement sets the Current Record Pointer to the
record whose relative record number is contained in the RELATIVE KEY data item specified in the SELECT clause, and makes the record available. If the file does not contain such a record, the INVALID KEY condition exists. The KEY IS clause may not be specified for a RRDS.

3. The START Statement:

This statement provides a means of positioning the Current Record Pointer within a KSDS or
RRDS for subsequent sequential record retrieval.

START file-name
KEY IS operand data-name-1
INVALID KEY statement-1
END START

where operand is: =,>,<,NOT<,>=,etc

Notes:
(1) When the KEY IS clause is specified, the Current Record Pointer is positioned at the logical
record in the file whose key field satisfies the comparison. When the KEY IS clause is not
specified, KEY IS EQUAL (to the prime record key or relative key) is implied.

(2) The comparison made during the execution of a START statement is between the current value in data-name-1 and the corresponding key field in the file's records.

(3) For a KSDS, data-name-1 can be any of the following:
- The prime RECORD KEY
- Any ALTERNATE RECORD KEY
- An alphanumeric data item which re-defines the record key for the file.

(4) When the START statement is successful, the RECORD KEY with which data-name-1 is
associated becomes the key of reference for subsequent READ statements.

(5) For a RRDS, when the KEY IS clause is specified, data-name-1 must be the relative key.

(6) If the FILE STATUS clause is specified in the FILE-CONTROL entry then the associated
"Status Code" is updated when the START statement is executed.

(7) If the comparison is not satisfied by any record in the file, an INVALID KEY condition exists and if specified statement-1 is executed.

4. The WRITE Statement

For an ESDS

WRITE record-name
FROM identifier-1
END-WRITE

For a KSDS and a RRDS

WRITE record-name
FROM identifier-1
INVALID KEY statement-1
END-WRITE

Notes:
(1) For the WRITE statement to be successful the data set must have been OPENED for
OUTPUT, I-O, or EXTEND.

(2) For a KSDS, before the WRITE statement is executed, the required value must be placed in the prime record key or alternate record key field. When ACCESS IS SEQUENTIAL, records must be released in ascending order of the record key values. When ACCESS IS RANDOM or
ACCESS IS DYNAMIC, records may be released in any programmer-specified order.

(3) For a KSDS, an INVALID KEY condition is caused by any of the following:
- ACCESS IS SEQUENTIAL and the file is opened for OUTPUT, and the value of the
prime record key is not greater than that of the previous record.
- The file is opened I-O and the value of the prime record key equals that of an already
existing record.
- An attempt is made to write beyond the externally defined boundaries of the file.
When an INVALID KEY condition is recognised, WRITE statement execution is unsuccessful and the contents of the record are unaffected.

(4) For a RRDS, when ACCESS IS SEQUENTIAL, the first record released has relative record
number 1, the second record released has a relative record number of 2 and so on. If ACCESS IS
RANDOM or ACCESS IS DYNAMIC the RELATIVE KEY must contain the desired relative
record number for the record before the WRITE statement is issued..

(5) The Current Record Pointer is not affected by the execution of a WRITE statement.

5. The REWRITE Statement:

This statement logically replaces an existing record in a direct-access file.

REWRITE record-name-1
FROM identifier-1
INVALID KEY statement-1
END-REWRITE

Notes:

(a) To use the REWRITE statement the data set must have been opened for I-O.

(b) For files in the sequential access mode, the input/output statement executed for this file must be a successfully executed READ statement. When the REWRITE statement is executed, the record retrieved by the READ statement is replaced.

(c) For a KSDS:
- when the access mode is sequential, the record to be replaced is specified by the value
contained in the prime record key. When the REWRITE statement is executed, this value
must equal the value of the prime record key data item in the last record READ from this
file.
- when the access mode is random or dynamic, the record to be replaced is specified by the value contained in the prime record key.
- values of alternate record keys in the rewritten record may differ form those in the record being replaced.
- an INVALID KEY condition exists when:
·  the access mode is sequential and the value contained in the prime record key of the record to be replaced does not equal the value of the prime record key data item of the last retrieved record from the file, or

·  the value contained in the prime record key does not equal that of any record in the file, or

·  the value of an alternative record key data item for which "duplicates" is not specified is
·  equal to that of a record already in the file.

(d) For a RRDS
- when the mode is sequential, the INVALID KEY clause may not be specified.
- when the access mode is random of dynamic, the record to be replaced is specified on in
the RELATIVE KEY data item. If the file does not contain the record specified, an invalid key condition exists and, if specified, statement-1 is executed.

(e) The Current Record Pointer is not affected by the execution of the REWRITE statement.

6. The DELETE Statement:

This statement removes a record from a KSDS or RRDS file. For indexed files, the space is then
immediately available for a new record. For relative files, the space is then available for new record with the same relative key value.

DELETE file-name RECORD
INVALID KEY statement-1
END-DELETE

Notes:

(a) To use the DELETE statement the data set must be opened in I-O mode.

(b) After successful execution of a DELETE statement, the record is removed from the file and can no longer be accessed.

(c) The Current Pointer Record is not affected by execution of the DELETE statement.

(d) For a file in sequential access mode, the last previous input/output statement must be a
successfully executed READ statement. When the DELETE statement is executed the system
removes the record retrieved by the read statement. The INVALID KEY clause must not be
specified for a file in this access mode.

(e) For a file in random or dynamic access mode, when the DELETE statement is executed, the
system removes the record identified by the contents of the prime record key for a KSDS, or the
relative key field for a RRDS. If the file does not contain such a record, an INVALID key
condition exists.

7. The CLOSE Statement

CLOSE file-name-1 WITH LOCK

Notes:
(a) If the WITH LOCK clause is specified the compiler ensures that this file cannot be opened again during this execution of the object program.