Adding a field to an existing COBOL data file - cobol

I have an existing MF COBOL 4.0 program with years of data in a ISAM file but I need to add a new field to the existing file. The record currently has 1208 chars and I need to add another 10 to it.
If I simply put the extra PIC X(10) field in my copybook, it gives me an error.

You need to modify the underlying data file to match your file definition in COBOL. One way to do so would be to define a line of output exactly like what lines of your data look like now, but with an extra Pic x(10) on the end of it. You would then read in your data line by line, and output it to a new location with 10 extra spaces on the end of it. That way your data is 10 characters longer, and you can go back and add that extra Pic x(10) to your main program. It should work after that.

With changing the copybook, you're only changing the representation of the data used in your program. Shouldn't you be restructuring the data source (i.e. the ISAM file) as well?

Late answer, but I thought you might be interested.
I've been working on our Cobol system for over 20 years and we've come across this issue many times.
Changes to the structure of our index files are what we consider a "Major Release". These require specific Conversion programs which:
Rename the physical file, moving it aside to an 'old' file
Open the 'old' version of the file (using a version of the copybook before the change)
Open (create) the 'new' version of the file
Move the contents of each of the 'old' records to a 'new' record and WRITEs it
Of course these conversions require the system to be 'down', hence the reason why they are considered major releases.
If you have files which are likely to have fields added to them in the future, you can add extra FILLER to the end of index file to let you cope with new fields being added. We tend to add a FILLER of 50 or 100. Of course this doesn't help you if you change one of the existing fields, or even the structure of any of the keys.

For file errors, you will want to keep a list handy. I recommend starting with a list you find online, and any time you get an error you cannot figure out in 5 seconds, add a detailed explanation of the resolution so you will have it in your notes the next time it happens. Here are a couple decent lists to start with
http://www.simotime.com/vsmfsk01.htm
http://www.briarcliff.edu/departments/cis/Cobol/Error%20Codes.html
In my list, file status 39 is:
OPEN-CONFLICT-FILE-ATR - The 'open' statement was unsuccessful because a conflict was detected between the fixed file attributes specified for that file in the program. These attibutes include the organization of the file (sequential, relative, or indexed), the prime record key, the alternate record keys, the code set, the maximum record size, and the record type (fixed or variable).
And this is from the personalized note: Check the file that you have assigned to your ddname in your JCL. Especially the length allocation. In your case, you know that the length does not match, since you just changed the program.
There are utilities to reformat datasets, particularly SYNCSORT. Or of course you can write your own.

Related

In Cobol why would you have a PERFORM THRU with non existent paragraph names

I am trying to figure out what the purpose of the PERFORM command is below. Code was written 20 years ago. ACPY-READ-FIRST, ACPY-READ-NEXT, and ACPY-EXIT don't exist anywhere in the program.
MOVE ACPY-ID TO WS-ACPY-ID.
PERFORM ACPY-READ-FIRST THRU ACPY-EXIT.
150-PYMTS.
PERFORM ACPY-READ-NEXT THRU ACPY-EXIT.
IF NOT SUCCESSFUL OR
ACCT-ID NOT = ACPY-ACCT-ID
GO TO 160-DONE.
Answer: You wouldn't as this would create a syntax error with every compiler.
The paragraphs (or even sections, but I'd look for the former) have to be somewhere in the source unit, I'd say: 95% likeliness to find it in a copybook named in the COPY statement (= COBOL's "include"), 4% that it is inserted by a code generator that was used to process this and 1% that you've just overlooked it (COBOL is case-insensitive, just in case).
Hint: If you have all necessary sources you can use GnuCOBOL to process it and create a listing which shows you the copybook it the paragraphs are included in.

How to get the current paragraph name?

I would like to know how to get the current paragraph name in COBOL (using MVS Enterprise COBOL V4.2 here).
Let's say I have this code in the PROCEDURE DIVISION :
MAIN-LOGIC.
MOVE SPACE TO ABT-MSG
PERFORM PARAGRAPH-1
PERFORM PARAGRAPH-2
GO TO CLOSE-PROGRAM.
*
* SEARCH FOR A VALUE IN AN ARRAY AND GET THE RELATED INDEX
*
PARAGRAPH-1.
MOVE 42 TO SEARCH-VALUE
PERFORM VARYING I-SEARCH FROM 1 BY 1
UNTIL SOME-ARRAY(I-SEARCH) = SEARCH-VALUE
IF (I-SEARCH = MAX-ARRAY-POSITION)
MOVE SEARCH-ABORT TO ABT-MSG
MOVE 'PARAGRAPH-1' TO ABT-LOC
GO TO CLOSE-PROGRAM
END-IF
END-PERFORM
DISPLAY 'VALUE WAS FOUND AT POSITION ' I-SEARCH '.'.
*
* STORE A NEW VALUE AT THE END OF AN ARRAY
*
PARAGRAPH-2.
MOVE 42 TO STORAGE-VALUE
ADD 1 TO I-STORAGE
IF (I-STORAGE > MAX-ARRAY-POSITION)
MOVE STORAGE-ABORT TO ABT-MSG
MOVE 'PARAGRAPH-2' TO ABT-LOC
GO TO CLOSE-PROGRAM
END-IF
MOVE STORAGE-VALUE TO SOME-ARRAY(I-STORAGE).
*
* CLOSE THE PROGRAM
*
CLOSE-PROGRAM.
IF ABT-MSG > SPACE
DISPLAY ABT-MSG
DISPLAY '(FOUND IN ' ABT-LOC ')'
MOVE 20 TO RETURN-CODE
ELSE
DISPLAY SUCCESS-MESSAGE
END-IF
STOP RUN.
I would like to be able to access the current paragraph name (and store it in ABT-LOC) instead of having to write it.
Is there a COBOL system variable to do so, like 'CURR-PARA-NAME' or something ?
Thank you.
------ UPDATE 1 -------
I have updated my code example to make it more specific.
Know that, in my real COBOL program, there are various occurences of SEARCH-ABORT and STORAGE-ABORT possibilities (I am working with many arrays).
I want to make my code as good as possible, hence my will to access the current paragraph name instead of having to write it.
Thank you again.
------- UPDATE 2 ------
Well then. It seems I cannot do it (the users of my program will probably reject any debug messages they are not used to get - For your information, I am rewriting a 50 years old program with very, very bad programming practices such as upward GO TOs, fall-through logic and the godforsaken ALTER, and I want to get the same output at the end).
Don't worry, I will not cry tonight. This was just an esthetical improvement to my code, and I can live without it (my code is already a lot prettier than what I based myself on).
I thank all of you for your time, and wish you a good... Stack Overday !
As Simon Sobisch has correctly indicated in his answer, the only way to do exactly what you want is to use the "debugging declaratives". See later in the answer for making that work, but no-one should allow you to do this to a Production program.
COBOL is a compiled language so there is no no automatic access to any data-name or procedure name (paragraph or SECTION) unless the compiler makes something available. Which, excluding the case above, it doesn't.
That leaves three approaches: doing it manually (which you correctly want to avoid, as sure as peaches someone is going to copy or relocate code without changing the literal); pre-processing (with a program or the editor) to automatically populate your field with the correct label; doing something else.
Since you are implicitly discounting the first, again I believe correctly, let's consider the second. What if you have two, three or eight things in the same paragraph/SECTION which are all "business errors" (although usually these types of things are more "integrity errors", a state which should not exist, so don't continue)?
Since you will get those, a "pre-processing" solution starts to get more ugly.
What else can be done?
Well, it's something we've faced for many, many years. The answer is, unique (within the program) error numbers. The individual errors can be named, well, and given a number. The well-named error reference is difficult to use "incorrectly". When adding a new error, it is difficult to duplicate an existing number. Or, to put it another way, it is easy to duplicate but horribly easy to spot in testing - "hey, that's produced 1234, that's wrong".
It's in no way bullet-proof, but the data-name (and any associated text) give better indication of the problem than a paragraph-name (which is not going to be, except artificially, any indication of what the error is, just the location of it). The error references are very easy to find in the program, and from that it is easy to locate the procedure name, except you don't actually need it any more.
Whether the program with error-numbers outweigh the dross of manually maintained MOVE 'literal' TO some-standard-name programs is unknown. But you can guess which I favour and recommend.
Now, how to do it for Enterprise COBOL with DECLARATIVES.
IDENTIFICATION DIVISION.
PROGRAM-ID. STAB39.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. FRED DEBUGGING MODE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-WHEN-COMPILED PIC X(8)BX(8).
01 ABT-LOC PIC X(30).
PROCEDURE DIVISION.
DDECLARATIVES.
DSOME-SECTION SECTION.
D USE FOR DEBUGGING ON ALL PROCEDURES
D .
DSOME-PARA.
D MOVE DEBUG-NAME TO ABT-LOC
D .
DEND DECLARATIVES.
STARTING-UP SECTION.
DISPLAY
ABT-LOC
D DISPLAY
D "IT IS STARTING UP"
MOVE WHEN-COMPILED TO W-WHEN-COMPILED
DISPLAY
"STAB39 "
W-WHEN-COMPILED
.
A-PARA.
DISPLAY
ABT-LOC
PERFORM
10 TIMES
D DISPLAY
"ITERATING"
END-PERFORM
.
ANOTHER-PARA.
DISPLAY
ABT-LOC
PERFORM THE-PARA
10 TIMES
PERFORM THE-SECOND-PARA
GOBACK
.
THE-PARA.
DISPLAY
ABT-LOC
.
THE-SECOND-PARA.
DISPLAY
ABT-LOC
.
Some notes:
The SOURCE-COMPUTER paragraph is required to use COBOLs in-built debugging features, to turn them on. So the ENVIRONMENT DIVISION and CONFIGURATION SECTION are also required. The "computer name", FRED in the example, is required, but it is irrelevant. You can "name" your computer after your favourite pet or relative if you like, or put anything there, there just has to be something.
DECLARATIVES can only be specified at the start of the PROCEDURE DIVISION. They must be within a SECTION and all actions must be within a paragraph belonging to a SECTION. The names of the SECTION and paragraph are irrelevant, but make them meaningful anyway.
Because the DECLARATIVES must contain a SECTION, you will be subject to an informational diagnostic message if your first procedure label is not also a SECTION. This does not require using SECTIONS over paragraphs in your program, it has no further effect.
The D in column seven indicates a "debugging line". These lines only get code generated when you turn debugging on with the SOURCE-COMPUTER paragraph.
The program exercises all use of a paragraph (and use of a SECTION is no different for this example) except GO TO. Paragraphs which are GO TO'd will produce the same results as any other reference, but you won't see GO TOs in my programs :-)
It is possible to name the procedure or procedures you want to "trap" with the DECLARATIVES instead of using "ALL PROCEDURES".
You may have multiple DEBUGGING procedures, and you can include extensive code within them if you wish (setting up conditions for testing, for instance).
Although this feature has existed in COBOL for a long time, it is probably fair to say that it is not widely used, especially as specific "debugging products" became available.
It is not enough just to have this program, the "run time" needs to have DEBUG turned on, if it is not the default. The run-time on z/OS is called Language Environment and is shared by multiple languages (allowing easy inter-language communication). Languages include C/C++, PL/I and Java as well as COBOL. There are Language Environment routines and macros available to make HLASM/Assembler programs "LE Compliant" to also provide ready interfacing.
To see what run-time options your site has as default, the easiest thing to do is to include a CEEOPTS DD statement in your run JCL.
//CEEOPTS DD *
RPTOPTS(ON)
This will list out all the options used for your "Enclave" (your run environment) and indicate where each option is sourced from.
If, in the OPTION column, you see NODEBUG, then COBOL debugging is turned off by default. To turn it on for a particular run:
//CEEOPTS DD *
DEBUG
This will allow all the D-labelled debugging lines and the debugging DECLARATIVES to execute.
This will do what you want, but no-one will allow a program with debugging on into Production, so you can't use it for what you want.
In order of preference, I advise error-numbers (and testing), automation, hand-coded procedure-name literals.
IBM fully documents all its products, and you can find the documentation (Language Reference and Programming Guide amongst others) for Enterprise COBOL V4.2 and also Language Environment (several) for your release of z/OS.
One final point. Don't use GO TO to "break out" of your normal processing flow. Use PERFORM. Even when, logically, the PERFORM cannot return. Using GO TO will turn off compiler optimisation for paragraphs/SECTIONs containing the GO TO which can easily cause a noticeable impact on execution. This is the reverse of the advice from before IBM COBOL ensured that the state of PERFORMed paragraphs/SECTIONs is not preserved between CALLs. At that time the correct advice was to use GO TO. It is no longer the correct advice.
As you have the pseudo-code "something bad happened here" I assume an exception. In this case the standard (COBOL 2002, COBOL 2014) function EXCEPTION-LOCATION may help (although the actual string is implementor-defined I assume the paragraph may be in there [GnuCOBOL for example has the format: program-id; paragraph [or paragraph OF section or section, depending on your program]; source-line]).
If your COBOL compiler provides this information in this function and there is no exception in the offending part already: create one via subtract 1 from unsigned-var or similar.
As Bill already said (or implied): this is a question where the actual COBOL compiler used will be the most important part if you must have the names in as identifier and as label or not.
Edit (after the actual COBOL compiler is known):
IBM MVS Enterprise COBOL doesn't have the EXCEPTION-LOCATION function. Therefore I see only one built-in solution:
DECLARATIVES.
debug-declaratives SECTION.
USE FOR DEBUGGING ON ALL PROCEDURES.
debug-par.
MOVE debug-name TO current-procedure.
END DECLARATIVES.
But as this is only active if your program runs in debugging-mode (which may causes a lot of debugging messages to occur) I don't suggest to actual use this.
Try to use an Editor providing macros (or run a shell script on your actual source) to create the source you pass to the compiler afterwards.

File status 23 on READ after START

My question is pertaining to a file status 23, which according to MicroFocus means that upon my attempt to READ from a .DAT file:
"Indicates no record found."
or
"Indicates a duplicate key condition. Attempt has been made to store a
record that would create a duplicate key in the indexed or relative
file or a duplicate alternate record key that does not allow
duplicates."
I have eliminated the fact that the latter is my issue because I'm allowing duplicates in this case.
The reason I'm stumped is that I'm using a START to navigate to the record inside of my .DAT file, and when I execute a READ just after the START has positioned my file pointer, I get the file status 23.
Here is my code:
900-GET-INST-ID.
OPEN INPUT INST-MST.
MOVE FALL-IN-INST TO INST-NAME-REC.
START INST-MST
KEY EQUAL TO INST-NAME-REC
INVALID KEY
DISPLAY "RECORD NOT FOUND"
NOT INVALID KEY
READ INST-MST
MOVE INST-ID-REC TO WS-INST-ID
END-START.
CLOSE INST-MST.
So when I am running this code my START successfully runs and goes into the NOT INVALID KEY block, and then the very next line executes and my read is null. How can this be if my alternate key (INST-NAME-REC) is actually found inside the .DAT?
I have ensured that my FD picture clauses match exactly in the ISAM Build program and in this program (the reading program).
The second reason you show is excluded not because you allow duplicate keys, but because that error message with that file-status is for a WRITE, and your failure is on a READ.
Here's your problem:
READ INST-MST
Here's how you fix it:
READ INST-MST NEXT
In COBOL 85, the READ statement has two formats. Format 1 is for a sequential read and Format 2 is for a keyed (random) read.
Unfortunately, the minimum READ syntax for both sequential and keyed reads is:
READ file-name
Which means if you use READ file-name the compiler will implicitly treat it as Format 1 or Format 2 depending on your SELECT statement.
READ file-name NEXT RECORD is identical to READ file-name NEXT.
Consult your actual documentation for a full explanation and discovery of possible Language Extensions from the vendor. If you consult closely, the behaviour of READ file-name with no further option depends on the type of file. With a keyed file, the default is a keyed READ. You key field (luckily) does not contain a key that exists, so you get the 23.
Even if it didn't work like that, what would be the point of not using the word NEXT? The compiler always knows what you tell it (which sometimes is not what you think you tell it), but in a situation like this, the human reader can be very unsure. The last thing you want to do when bug-hunting is break off to look at the manual to discover exactly how that behaves, and then try to work it if that behaviour was the one sought by the original coder. The bug? A bug? Intended, but sloppy, code? No-one wants to spend that time, and look, even now, it is you.
A couple of comments on your code.
Look up the FILE STATUS clause of the SELECT. Use it. One field per file. Check after each IO. It'll save you grief.
Once using the FILE STATUS, ditch the imperative parts of the IO statements (the something/NOT something) and replace by tests of the file-status field (using 88s).
It looks like you are OPENing and CLOSEing your look-up file all the time. Please don't. OPEN and CLOSE can be very heavy and time-consuming, so do them once per program per file. If you've done that because of a problem, find a correct resolution to that problem, don't use a hack.
Drop the full-stops/periods except where they are needed. This is COBOL 85, which means for 30 years the number of full-stops/periods required in the PROCEDURE DIVISION have been greatly reduced. Get modern, and take advantage of that, it'll save you Gotcha!s as you copy/paste code, leaving the one which shouldn't be there and changing the way the program behaves.

Why is the organization clause not working?

I am using VSAM files for the first time and am having a ton of trouble with the ORGANIZATION part of the VSAM definition.
I thought that the key is what Cobol uses to search on the file.
Question: Why is the organization clause not working?
Code Here:
SELECT SW24VF01 ASSIGN TO UT-S-INPUT2
ORGANIZATION IS INDEXED
ACCESS IS RANDOM
RECORD KEY IS MY-PROV-KEY-24
FILE STATUS IS SW24VF01-STAT.
SELECT SS45VF90 ASSIGN TO UT-S-INPUT3
ORGANIZATION IS INDEXED
ACCESS IS RANDOM
RECORD KEY IS MY-PROV-KEY-45
FILE STATUS IS SS45VF90-STAT.
FD SW24VF01.
01 MY-PROV-KEY-24 PIC X(09).
01 FILLER PIC X(1991).
FD SS45VF90.
01 MY-PROV-KEY-45 PIC X(09).
01 FILLER PIC X(1991).
Error Here:
32 IGYGR1208-E The "ORGANIZATION" clause for file "SW24VF01" specified an
organization specified in the assignment-name. An organiz
37 IGYGR1208-E The "ORGANIZATION" clause for file "SS45VF90" specified an
organization specified in the assignment-name. An organiz
You need to get hold of a copy of the Enterprise COBOL Language Reference for the version of Enterprise COBOL you are using. This will most likely be V3.4, V4.1 or V4.2, possibly V5.1 (released in June last year).
For this particular question, any version will do, but going forward it is good to know where to find the one specific to the version of COBOL you are using. Also get the Programming Guide as well.
An internet search for IBM Enterprise COBOL for z/OS library should get you to a page where you can easily locate the correct one.
Locate the ASSIGN clause in the Language Reference.
Format: assignment-name for QSAM files
label-S-name
Format: > assignment-name for VSAM sequential file
label-AS-name
Format: > assignment-name for line-sequential, VSAM indexed, or VSAM relative
file
label-name
label- Documents (for the programmer) the device and device class to
which a file is assigned. It must end in a hyphen; the specified value
is not otherwise checked. It has no effect on the execution of the
program. If specified, it must end with a hyphen.
S- For QSAM files, the S- (organization) field can be omitted. AS- For
VSAM sequential files, the AS- (organization) field must be specified.
**For VSAM indexed and relative files, the organization field must be omitted.**
name
A required field that specifies the external name for this file.
Thus, a VSAM KSDS or RRDS requires that the ASSIGN contains very limited information, a label, which is syntax-checked but otherwise not used.
In the past, information in the ASSIGN would have had many more meanings. A different COBOL on a different Operating System may have different requirements. For Enterprise COBOL for z/OS, the ASSIGN is very simple.
In the Programming Guide you will find a Chapter on processing VSAM files. It will assist you in this task.
From that Chapter, an example SELECT statement for a KSDS:
SELECT R-FILE
ASSIGN TO RELATIVE-FILE
ORGANIZATION IS RELATIVE
ACCESS IS RANDOM
RELATIVE KEY IS RFILE-RELATIVE-KEY
FILE STATUS IS FSTAT-CODE VSAM-CODE.
You should take note of that second data-name for the FILE STATUS. This is an extension to the simple FILE STATUS, just for VSAM files. It will contain diagnostic information when you have a non-zero FILE STATUS. I suggest you code it, it will make things easier for you.

reading and sorting a variable length CSV file

We am using OpenVMS system and I believe it is using the Cobol from HP.
With a data file of a lot of records ( 500mb or more ) which variable length. The records are comma delimited. I would like to parse each records and extract corresponding fields for processing. After that, I might want to sort it by some particular fields. Is it possible with cobol?
I've seen sorting with fixed-length records only.
Variable length is no problem, not sure exactly how this is done in VMS cobol but the IBMese for this is:-
FILE SECTION.
FD THE-FILE RECORD IS VARYING DEPENDING ON REC-LENGTH.
01 THE-RECORD PICTURE X(5000) .
WORKING-STORAGE SECTION.
01 REC-LENGTH PICTURE 9(5) COMPUTATIONAL.
When you read the file "REC-LENGTH" will contain the record length, when write a record it will write a record of length REC-LENGTH.
To handle the delimited record files you will probably need to use the "UNSTRING" verb to convert into a fixed format. This is pretty verbose (but then this is COBOL).
UNSTRING record DELIMITED BY ","
INTO field1, field2, field3, field4, field5 etc....
END-UNSTRING
Once the record is in fixed format you can use the SORT as normal.
The Cobol SORT verb will do what you need.
If the SD file contains variable-length records, all of the KEY data-items must be contained within the first n character positions of the record, where n equals the minimum records size
specified for the file. In other words, they have to be in the fixed part.
However, you can get around this easily by using an input procedure. This will let you create a virtual file that has its keys in the right place. In your input procedure, you will reformat your variable, comma delimited, record, into one that has its keys at the front, then "Release" it to the sort.
If my memory is correct, VMS has a SORT/MERGE utility that you could use after you have processed the file into a fixed file format (variable may also be possible). Typically a standalone SORT utility performs better than in-line COLBOL SORT and can be better design if the sort criteria changes in the future.
No need to write a solution in COBOL, at least not to sort the file. The UNIX sort utility should do it just fine, just call sort -t ',' -n with maybe a couple of other options.

Resources