COBOL indexed files: ORGANIZATION INDEXED, START, READ KEY, REWRITE
A GnuCOBOL program loads a parts master into an indexed file, reads by key, rewrites a record, browses a key range with START and READ NEXT, and deletes; data ships.
A sequential file gives you the records in the order they were written and nothing else. An indexed file gives you a record by its key, the records in key order from any starting point, and the ability to replace or remove one record in place. That is what a master file is, and most of the COBOL that runs a business is a batch program reading transactions sequentially and applying them to a master file by key. This tutorial builds a small parts master from a fixed-width file, then performs every operation the file organization exists for: a random READ by key, a READ followed by REWRITE, a key-range browse with START and READ NEXT, a browse on an alternate key, a DELETE, and a final pass from the lowest key to end of file. The load step reads its input exactly as reading a sequential file to end of file did, and the table of look-up keys is the REDEFINES pattern of three literals viewed as one OCCURS.
The listing was desk-checked against the GnuCOBOL Programmer's Guide and the IBM Enterprise COBOL 6.4 Language Reference, and the expected output quoted below came from the Python companion, which writes the data file and replays every step of the program against an ordinary dictionary. On 29 October 2026 we compiled and ran the program with GnuCOBOL 3.2, built with the Berkeley DB handler for indexed files (cobc --info reports "indexed file handler : BDB"). It printed the output below exactly, once one status code the companion had taken from IBM's table was corrected; the REWRITE section explains it. Where the two compilers document the same statement differently, the difference is stated with its source rather than smoothed over.
The SELECT that makes a file indexed
SELECT Parts-File
ASSIGN TO "parts-master"
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS Part-No
ALTERNATE RECORD KEY IS Part-Bin WITH DUPLICATES
FILE STATUS IS WS-Parts-Status.
Four clauses do the work. ORGANIZATION IS INDEXED says records are located by the value of a field inside them; section 5.2.1.4 of the Programmer's Guide puts it as "the actual location of a record in an indexed file is calculated automatically based upon the value(s) of one or more alphanumeric fields within records of the file". RECORD KEY IS Part-No names that field, and the guide adds that "No two records in the file will be allowed to have the same PRIMARY KEY field value". IBM says the same on page 150 of the Language Reference: the values "must be unique among records in the file", and whatever the key's category, "the key is treated as an alphanumeric item" whose collation is its binary value order.
ACCESS MODE IS DYNAMIC is the choice that lets one program do both kinds of reading. The guide's description is that DYNAMIC "allows the file will be processed either in RANDOM or SEQUENTIAL mode; the program may switch between the two modes as needed. The START statement is used to make the switch between modes". With ACCESS MODE SEQUENTIAL, the default, only READ NEXT is available; with RANDOM, only READ by key. IBM's ACCESS MODE clause on page 148 describes DYNAMIC identically, and the next page adds a performance note: dynamic access "is tuned for positioning to a record and then performing sequential reads", so a program that only ever reads by key should say RANDOM.
ALTERNATE RECORD KEY IS Part-Bin WITH DUPLICATES builds a second index, on the storage bin, in which several parts may share a value. Both documents describe the alternate key as a second path for direct access and for sequential processing; IBM's WRITE statement page (477) adds that with DUPLICATES "the system stores the records so that later sequential access to the records allows retrieval in the same order in which they were stored".
The record itself is the copybook /code/cobol-indexed-file-start-read-key-rewrite.cpy, 48 bytes:
| Field | Positions | PIC | Role |
|---|---|---|---|
| Part-No | 1 to 8 | X(8) | Prime record key, unique |
| Part-Desc | 9 to 32 | X(24) | Description |
| Part-Bin | 33 to 36 | X(4) | Alternate record key, duplicates allowed |
| Part-On-Hand | 37 to 41 | 9(5) | Quantity on hand |
| Part-Unit-Cost | 42 to 48 | 9(5)V99 | Unit cost, implied decimal point |
The key is alphanumeric on purpose. IBM's START page carries a warning that a numeric key must be searched with EQUAL TO only, "otherwise, unexpected results can happen", and the guide describes indexed keys as alphanumeric fields throughout. Part numbers like BRK-0210 sort as text, which is what a range browse on a prefix needs.
The program
>>SOURCE FORMAT IS FREE
*> cobol-indexed-file.cob — build an indexed parts
*> master from a fixed-width file, then read it by key, rewrite
*> a record, browse a key range with START, browse an alternate
*> key, delete a record, and take a final sequential pass
*> Dialect: GnuCOBOL 3.x, free format
*> Status : desk-checked; compiled and run with GnuCOBOL 3.2 on 2026-10-29
IDENTIFICATION DIVISION.
PROGRAM-ID. PARTS-MASTER-DEMO.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT Load-File
ASSIGN TO "datasets/cobol-indexed-file-start-read-key-rewrite.dat"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-Load-Status.
SELECT Parts-File
ASSIGN TO "parts-master"
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS Part-No
ALTERNATE RECORD KEY IS Part-Bin WITH DUPLICATES
FILE STATUS IS WS-Parts-Status.
DATA DIVISION.
FILE SECTION.
FD Load-File.
01 Load-Line PIC X(48).
FD Parts-File.
COPY "cobol-indexed-file-start-read-key-rewrite.cpy".
WORKING-STORAGE SECTION.
77 WS-Load-Status PIC XX.
77 WS-Parts-Status PIC XX.
01 WS-Flags.
05 WS-EOF-Flag PIC X VALUE "N".
88 End-Of-File VALUE "Y".
05 WS-Browse-Flag PIC X VALUE "N".
88 Browse-Done VALUE "Y".
77 WS-Loaded PIC 9(5) VALUE 0.
77 WS-Rejected PIC 9(5) VALUE 0.
77 WS-Count PIC 9(5) VALUE 0.
77 WS-Before PIC 9(5) VALUE 0.
77 WS-Value PIC 9(9)V99 VALUE 0.
77 WS-Total PIC 9(9)V99 VALUE 0.
77 WS-Show-Count PIC ZZ,ZZ9.
77 WS-Show-Before PIC ZZ,ZZ9.
77 WS-Show-Money PIC Z,ZZZ,ZZ9.99.
77 WS-Search-Key PIC X(8).
77 WS-Search-Bin PIC X(4).
01 WS-Lookup-Keys.
05 FILLER PIC X(8) VALUE "FLT-0305".
05 FILLER PIC X(8) VALUE "GSK-0042".
05 FILLER PIC X(8) VALUE "BRK-9999".
01 WS-Lookup-Table REDEFINES WS-Lookup-Keys.
05 WS-Lookup-Key PIC X(8) OCCURS 3 TIMES.
77 WS-Lookup-Ix PIC 9 VALUE 0.
77 WS-Receive-Key PIC X(8) VALUE "BRK-0210".
77 WS-Receive-Qty PIC 9(5) VALUE 25.
77 WS-Ghost-Key PIC X(8) VALUE "ZZZ-0000".
77 WS-Browse-Prefix PIC X(4) VALUE "BRK-".
77 WS-Browse-Bin PIC X(4) VALUE "B-07".
77 WS-Delete-Key PIC X(8) VALUE "GSK-0042".
PROCEDURE DIVISION.
Main-Paragraph.
PERFORM Load-Master
OPEN I-O Parts-File
IF WS-Parts-Status NOT = "00"
DISPLAY "Cannot open parts master for I-O, status " WS-Parts-Status
STOP RUN
END-IF
PERFORM Look-Up-Keys
PERFORM Receive-Stock
PERFORM Rewrite-Ghost
PERFORM Browse-Prefix
PERFORM Browse-Bin
PERFORM Delete-Part
PERFORM Final-Pass
CLOSE Parts-File
IF WS-Parts-Status NOT = "00"
DISPLAY "Close failed, status " WS-Parts-Status
END-IF
STOP RUN.
Load-Master.
OPEN INPUT Load-File
IF WS-Load-Status NOT = "00"
DISPLAY "Cannot open the load file, status " WS-Load-Status
STOP RUN
END-IF
OPEN OUTPUT Parts-File
IF WS-Parts-Status NOT = "00"
DISPLAY "Cannot create the parts master, status " WS-Parts-Status
STOP RUN
END-IF
PERFORM Read-Load-Line
PERFORM UNTIL End-Of-File
MOVE Load-Line TO Part-Record
WRITE Part-Record
INVALID KEY
ADD 1 TO WS-Rejected
DISPLAY "Load: duplicate key " Part-No
" rejected, status " WS-Parts-Status
NOT INVALID KEY
ADD 1 TO WS-Loaded
END-WRITE
PERFORM Read-Load-Line
END-PERFORM
CLOSE Load-File Parts-File
MOVE WS-Loaded TO WS-Show-Count
MOVE WS-Rejected TO WS-Show-Before
DISPLAY "Loaded " WS-Show-Count " parts, rejected " WS-Show-Before.
Read-Load-Line.
READ Load-File
AT END
SET End-Of-File TO TRUE
END-READ
IF WS-Load-Status NOT = "00" AND WS-Load-Status NOT = "10"
DISPLAY "Load file read failed, status " WS-Load-Status
STOP RUN
END-IF.
Look-Up-Keys.
PERFORM VARYING WS-Lookup-Ix FROM 1 BY 1 UNTIL WS-Lookup-Ix > 3
MOVE WS-Lookup-Key(WS-Lookup-Ix) TO WS-Search-Key
PERFORM Look-Up-One
END-PERFORM.
Look-Up-One.
MOVE WS-Search-Key TO Part-No
READ Parts-File
INVALID KEY
DISPLAY "Look-up " WS-Search-Key
": not found, status " WS-Parts-Status
NOT INVALID KEY
MOVE Part-On-Hand TO WS-Show-Count
DISPLAY "Look-up " Part-No ": " Part-Desc
" bin " Part-Bin " on hand " WS-Show-Count
END-READ.
Receive-Stock.
MOVE WS-Receive-Key TO Part-No
READ Parts-File
INVALID KEY
DISPLAY "Receive " WS-Receive-Key
": not found, status " WS-Parts-Status
NOT INVALID KEY
MOVE Part-On-Hand TO WS-Before
ADD WS-Receive-Qty TO Part-On-Hand
REWRITE Part-Record
INVALID KEY
DISPLAY "Receive " WS-Receive-Key
": rewrite failed, status " WS-Parts-Status
NOT INVALID KEY
MOVE WS-Before TO WS-Show-Before
MOVE Part-On-Hand TO WS-Show-Count
DISPLAY "Receive " Part-No ": on hand " WS-Show-Before
" -> " WS-Show-Count ", rewrite status "
WS-Parts-Status
END-REWRITE
END-READ.
Rewrite-Ghost.
MOVE SPACES TO Part-Record
MOVE WS-Ghost-Key TO Part-No
MOVE "GHOST" TO Part-Desc
MOVE "Z-99" TO Part-Bin
MOVE 0 TO Part-On-Hand
MOVE 0 TO Part-Unit-Cost
REWRITE Part-Record
INVALID KEY
DISPLAY "Rewrite " WS-Ghost-Key
": no such record, status " WS-Parts-Status
NOT INVALID KEY
DISPLAY "Rewrite " WS-Ghost-Key ": unexpectedly succeeded"
END-REWRITE.
Browse-Prefix.
MOVE 0 TO WS-Count WS-Total
MOVE "N" TO WS-Browse-Flag
MOVE WS-Browse-Prefix TO Part-No
START Parts-File KEY IS GREATER THAN OR EQUAL TO Part-No
INVALID KEY
DISPLAY "Browse " WS-Browse-Prefix ": nothing at or after it, status "
WS-Parts-Status
SET Browse-Done TO TRUE
END-START
PERFORM UNTIL Browse-Done
READ Parts-File NEXT RECORD
AT END
SET Browse-Done TO TRUE
NOT AT END
IF Part-No(1:4) = WS-Browse-Prefix
ADD 1 TO WS-Count
COMPUTE WS-Value = Part-On-Hand * Part-Unit-Cost
ADD WS-Value TO WS-Total
MOVE Part-On-Hand TO WS-Show-Count
MOVE WS-Value TO WS-Show-Money
DISPLAY " " Part-No " " Part-Desc " " WS-Show-Count
" " WS-Show-Money
ELSE
SET Browse-Done TO TRUE
END-IF
END-READ
END-PERFORM
MOVE WS-Count TO WS-Show-Count
MOVE WS-Total TO WS-Show-Money
DISPLAY "Browse " WS-Browse-Prefix ": " WS-Show-Count
" parts, stock value " WS-Show-Money.
Browse-Bin.
MOVE 0 TO WS-Count
MOVE "N" TO WS-Browse-Flag
MOVE WS-Browse-Bin TO Part-Bin
START Parts-File KEY IS GREATER THAN OR EQUAL TO Part-Bin
INVALID KEY
DISPLAY "Bin " WS-Browse-Bin ": nothing at or after it, status "
WS-Parts-Status
SET Browse-Done TO TRUE
END-START
PERFORM UNTIL Browse-Done
READ Parts-File NEXT RECORD
AT END
SET Browse-Done TO TRUE
NOT AT END
IF Part-Bin = WS-Browse-Bin
ADD 1 TO WS-Count
MOVE Part-On-Hand TO WS-Show-Count
DISPLAY " " Part-Bin " " Part-No " " Part-Desc " "
WS-Show-Count
ELSE
SET Browse-Done TO TRUE
END-IF
END-READ
END-PERFORM
MOVE WS-Count TO WS-Show-Count
DISPLAY "Bin " WS-Browse-Bin ": " WS-Show-Count " parts".
Delete-Part.
MOVE WS-Delete-Key TO Part-No
DELETE Parts-File RECORD
INVALID KEY
DISPLAY "Delete " WS-Delete-Key ": not found, status "
WS-Parts-Status
NOT INVALID KEY
DISPLAY "Delete " WS-Delete-Key ": status " WS-Parts-Status
END-DELETE
MOVE WS-Delete-Key TO WS-Search-Key
PERFORM Look-Up-One.
Final-Pass.
MOVE 0 TO WS-Count WS-Total
MOVE "N" TO WS-Browse-Flag
MOVE LOW-VALUES TO Part-No
START Parts-File KEY IS GREATER THAN OR EQUAL TO Part-No
INVALID KEY
DISPLAY "Final pass: file is empty, status " WS-Parts-Status
SET Browse-Done TO TRUE
END-START
PERFORM UNTIL Browse-Done
READ Parts-File NEXT RECORD
AT END
SET Browse-Done TO TRUE
NOT AT END
ADD 1 TO WS-Count
COMPUTE WS-Value = Part-On-Hand * Part-Unit-Cost
ADD WS-Value TO WS-Total
END-READ
END-PERFORM
MOVE WS-Count TO WS-Show-Count
MOVE WS-Total TO WS-Show-Money
DISPLAY "Final pass: " WS-Show-Count " parts, stock value "
WS-Show-Money.
The shipped listing carries a fuller header comment with build and run lines; the body is identical.
Loading: OPEN OUTPUT and WRITE
An indexed file is created by opening it for OUTPUT and writing records. Section 7.8.28 of the guide describes OUTPUT as writing "new content (which will completely replace any previous file contents)", so running the program twice starts clean each time. Under DYNAMIC access the records may arrive in any order; IBM's WRITE page states that only "When ACCESS IS SEQUENTIAL is specified in the file-control entry, records must be released in ascending order of RECORD KEY values". The load file happens to be in key order anyway.
The load file carries 21 lines, and one of them, the second CBL-1150, has a key that is already in the file. Both compilers refuse it. The guide's status list gives 22 as "Key already exists"; IBM's table on page 301 says 22 means "an attempt was made to write or rewrite a record that would create a duplicate prime record key or a duplicate alternate record key without the DUPLICATES phrase in an indexed file". The INVALID KEY branch counts it and moves on, which is the right behaviour for a load: reject the line, keep going, report the count at the end.
READ by key and the file status codes that matter
A random READ has no NEXT. The value to find goes into the RECORD KEY field, and the statement either fills the record area or raises INVALID KEY. Section 7.8.30.2 of the guide is precise about the absent KEY phrase: for an indexed file "the contents of the field declared as the file's RECORD KEY will be used to identify a record". The same section makes a point that catches people writing DYNAMIC-mode programs: the bare statement READ file-name is legal in both formats, so to get a sequential read you "must add either NEXT or PREVIOUS to the statement (otherwise it will be treated as a random READ)". IBM says the same from the other side, on page 428: "You must specify the NEXT RECORD phrase to retrieve records sequentially from files in dynamic access mode".
The look-up of BRK-9999 fails with status 23. The guide's list reads "23 Key not found"; IBM's table reads "An attempt was made to randomly access a record that does not exist in the file". A program that reads a master by key checks for exactly two outcomes, 00 and 23, and treats anything else as an error worth stopping for. The codes that appear in this program:
| Status | GnuCOBOL Programmer's Guide (5.2.1 SELECT) | IBM Enterprise COBOL 6.4 (LR pp. 300-302) |
|---|---|---|
| 00 | Success | Successful completion, no further information |
| 02 | Success (Duplicate Record Key Written) | Also set on a READ when the next record carries the same alternate key value |
| 10 | End of file reached if reading forward | A sequential READ was attempted and no next logical record existed |
| 21 | Key invalid | Sequence error: the prime key was changed between a READ and the REWRITE, or ascending order was violated on a sequential-access WRITE; GnuCOBOL 3.2 with the BDB handler also returns it for a REWRITE whose key matches no record |
| 22 | Key already exists | Duplicate prime key, or duplicate alternate key without DUPLICATES |
| 23 | Key not found | Random access to a record that does not exist; also a START on an unavailable optional file |
| 35 | File not found | OPEN INPUT, I-O or EXTEND on a nonoptional file that was unavailable |
| 43 | Read not done | In sequential access mode, the statement before a DELETE or REWRITE was not a successful READ |
The 02 row is a genuine difference in what the documents say. The guide's wording covers only a write that created a duplicate alternate key; IBM's table says the code is also returned by a READ when "the key value for the current key of reference was equal to the value of the same key in the next record". A browse on Part-Bin in this program would therefore see 02 rather than 00 on z/OS for every record but the last in a bin, which is why the NOT AT END branch tests nothing about the status and the loop ends only on AT END or a change of bin.
REWRITE: read it, change it, put it back
Receive-Stock is the update pattern: READ by key, change a field, REWRITE the same record. The guide's section 7.8.35 requires the file to be "currently open for I-O", and IBM's REWRITE page says the same, adding that the record "is logically replaced". The prime key is left alone; only Part-On-Hand changes, from 42 to 67.
What identifies the record being replaced depends on the access mode, and the two documents agree on the rule while stating it differently. For DYNAMIC or RANDOM access, the guide says "no READ is required before a record may be rewritten — the RELATIVE KEY or RECORD KEY definition for the file, respectively, will specify the record to be updated". IBM: "When the access mode is random or dynamic, the record to be replaced is specified by the value contained in the prime RECORD KEY". Rewrite-Ghost leans on that: it fills the record area with a key nobody has and issues a REWRITE with no READ before it. IBM lists this case explicitly as an invalid key condition, "The value contained in the prime RECORD KEY does not equal that of any record in the file", and IBM's table gives it status 23. GnuCOBOL 3.2 takes the INVALID KEY branch too, but with status 21: that is what the program printed, and a short test program got 21 for the same kind of REWRITE with no READ before it, after a successful READ, and after a failed one. We ran only the BDB handler. A program meant for both compilers should treat 21 and 23 alike at this point.
Under SEQUENTIAL access the rules tighten. The record rewritten is the one from the last READ, and IBM's page 433 adds a rule the guide does not spell out: the key value at REWRITE time "must equal the value of the prime record key data item in the last record read from this file", failing with status 21 otherwise. A REWRITE that is not preceded by a successful READ gets 43 in both dialects. One more difference in what is documented: the guide notes that a rewrite "does not cause the contents of the file to be physically updated until the next block of the file is read, a COMMIT or UNLOCK statement is issued or that file is closed", and we found no equivalent statement in the IBM reference.
START and READ NEXT: the key-range browse
The browse is the operation that makes indexed files worth the trouble. Browse-Prefix wants every part whose number begins BRK-. It moves the four-character prefix into the eight-character key, which pads it with spaces; a space sorts below every digit and letter in both ASCII and EBCDIC, so BRK- followed by four spaces is lower than any real BRK part number and higher than any BLT one. START ... KEY IS GREATER THAN OR EQUAL TO Part-No then positions on the first BRK record. The guide describes START as positioning "an internal logical record pointer to a particular record in the file, but does not actually transfer any of that record's data into the record buffer"; IBM's phrasing is that the file position indicator moves to "the logical record in the file whose key field satisfies the comparison". Data only arrives with the READ NEXT that follows, and the loop stops when a key with a different prefix comes back or the file ends.
The worked numbers, from the Python companion: five BRK parts, and because the rewrite already ran, BRK-0210 shows 67 on hand and a value of 1,232.80. The five values add to 3,265.45.
BRK-0210 BRAKE PAD SET FRONT 67 1,232.80
BRK-0211 BRAKE PAD SET REAR 37 627.15
BRK-0330 BRAKE DISC 280MM 12 528.00
BRK-0331 BRAKE DISC 300MM 9 472.50
BRK-0500 BRAKE FLUID DOT4 500ML 60 405.00
Browse BRK-: 5 parts, stock value 3,265.45
Browse-Bin is the same shape on the alternate key. The START names Part-Bin, which changes the order READ NEXT travels in: the guide says the sequence follows "the RECORD KEY or the ALTERNATE RECORD KEY most-recently referenced on a START statement", and IBM says the key named in a successful START "becomes the key of reference for subsequent READ statements". Bin B-07 holds three parts, two brake pad sets and a spark plug, and they come back in the order they were written.
Two things about START differ between the compilers and are worth knowing before writing one for both. First, the relation operators. The guide's syntax for 3.x lists EQUAL TO, GREATER THAN, GREATER THAN OR EQUAL TO, NOT LESS THAN, LESS THAN, LESS THAN OR EQUAL TO and NOT GREATER THAN, plus FIRST and LAST, and explains that the "less than" family positions on the last record satisfying the condition, searching last-to-first. The IBM 6.4 format on page 455 lists EQUAL TO, GREATER THAN, NOT LESS THAN and GREATER THAN OR EQUAL TO, and nothing else; there is no LESS THAN, no FIRST, no LAST. The program uses GREATER THAN OR EQUAL TO everywhere, and Final-Pass positions on the first record by moving LOW-VALUES into the key rather than saying START Parts-File FIRST, which GnuCOBOL accepts and IBM does not.
Second, what the KEY operand may be. The guide says "identifier-1 must be the defined RECORD KEY of the file or any of the ALTERNATE RECORD KEY fields for the file". IBM allows those and one more: "A data item within a record description for a file whose leftmost character position corresponds to the leftmost character position of that record key", no longer than the key, with the comparison truncated to the shorter length. That is the classic mainframe generic-key START, where a four-byte field overlaying the front of the key is named directly. The space-padding trick in Browse-Prefix gets the same effect with the full key field, so it works under both.
READ PREVIOUS, which the guide offers for indexed files, has no counterpart in the IBM 6.4 READ statement; a program that needs it is GnuCOBOL-only.
DELETE
Delete-Part removes GSK-0042. Under DYNAMIC access the guide says "the record to be deleted is the one whose primary key is currently the value of the field specified as the RECORD KEY", with no READ required; IBM's page 332 matches, and adds that the record area is not disturbed and that "the key can then be reused for record addition". Both say INVALID KEY may not be coded when the access mode is SEQUENTIAL, where the record deleted is the one from the preceding READ. The look-up that follows the DELETE returns 23, which is the proof that it is gone.
What comes out
The full output, produced by running python code/cobol-indexed-file-start-read-key-rewrite.py and, line for line, by the compiled program:
Load: duplicate key CBL-1150 rejected, status 22
Loaded 20 parts, rejected 1
Look-up FLT-0305: AIR FILTER PANEL bin F-01 on hand 96
Look-up GSK-0042: HEAD GASKET 1.6L bin G-05 on hand 6
Look-up BRK-9999: not found, status 23
Receive BRK-0210: on hand 42 -> 67, rewrite status 00
Rewrite ZZZ-0000: no such record, status 21
BRK-0210 BRAKE PAD SET FRONT 67 1,232.80
BRK-0211 BRAKE PAD SET REAR 37 627.15
BRK-0330 BRAKE DISC 280MM 12 528.00
BRK-0331 BRAKE DISC 300MM 9 472.50
BRK-0500 BRAKE FLUID DOT4 500ML 60 405.00
Browse BRK-: 5 parts, stock value 3,265.45
B-07 BRK-0210 BRAKE PAD SET FRONT 67
B-07 BRK-0211 BRAKE PAD SET REAR 37
B-07 SPK-0010 SPARK PLUG COPPER 400
Bin B-07: 3 parts
Delete GSK-0042: status 00
Look-up GSK-0042: not found, status 23
Final pass: 19 parts, stock value 7,709.80
The final pass counts 19 parts: 21 lines loaded, one rejected as a duplicate, one deleted. The stock value of 7,709.80 includes the 25 received units of BRK-0210 at 18.40 and excludes the six head gaskets at 38.00 that were deleted. Every edited field is printed through the same pictures the COBOL uses, ZZ,ZZ9 for counts and Z,ZZZ,ZZ9.99 for money, and the compiled program's lines matched the Python lines byte for byte.
Compile and run
python code/cobol-indexed-file-start-read-key-rewrite.py
cobc -x -I code -o parts-master-demo code/cobol-indexed-file.cob
./parts-master-demo
Run all three from the folder that holds code/ and datasets/. ASSIGN TO "parts-master" is a relative name, and the GnuCOBOL Manual's runtime configuration documents COB_FILE_PATH as the "default location where data files are stored", defaulting to the current directory, so the indexed file lands beside the executable unless that variable says otherwise.
Limitations
What the indexed file looks like on disk is not something the Programmer's Guide states. Our GnuCOBOL 3.2 run, with the BDB handler, left two files in the folder, parts-master and parts-master.1. The 3.x NEWS file names the backend in its 3.2 entry, "ORGANIZATION INDEXED (with BDB backend): internal changes in record and file locking", and its planned 3.3 entry adds COLLATING SEQUENCE on indexed files "currently only with the BDB backend", so the file format belongs to whichever library the build was configured with, and a file written by one build should not be assumed readable by another. On z/OS the equivalent is a VSAM key-sequenced data set, which IBM's Programming Guide lists as supporting sequential, random and dynamic access; how such a data set is defined before the program runs is outside this lesson.
The guide used throughout says on its own first page that it documents the July 2020 build of GnuCOBOL 3.1 RC-1, so anything about indexed files that changed in 3.2 would not appear in it. The record-locking clauses on READ and REWRITE, and the note that the file is not physically updated until a block is read or the file is closed, are in the guide but were not exercised here, and a program that shares a master with other processes needs them.
Finally, the load step trusts the data file. Nothing checks that Part-On-Hand and Part-Unit-Cost hold digits before they are moved into PIC 9 fields; a non-numeric byte there would corrupt the arithmetic in the browse without any file status telling you so. The input validation lesson later in the queue is where that check belongs.
Correction, 29 October 2026: the ghost REWRITE was shown failing with status 23, the code IBM documents for a REWRITE whose key matches no record. A real GnuCOBOL 3.2 run returns 21, so the output line, the companion that printed it and the REWRITE section are corrected, and the status table notes it. The build command shown before also named the source cobol-indexed-file-start-read-key-rewrite.cob, and GnuCOBOL 3.2's cobc refuses a source file whose base name is longer than 31 characters ("invalid file base name ... length exceeds maximum"), even with -o, so it failed as printed; the listing is now shipped as cobol-indexed-file.cob, and the old download address redirects there. With the status corrected, the output shown matches a real run line for line, so the page now says the program was compiled and run.
Index terms cobol, gnucobol, indexed file, start, rewrite