QSAM Functions

The Shadow REXXTOOLS QSAM interface gives REXX programmers access to sequential files and partitioned data set members using the Queued Sequential Access Method (QSAM). It provides several capabilities not provided by the EXECIO command:

QSAM Basics Record Formats
Programming for QSAM Allocating Data Sets
Opening and Closing Data Sets Reading, Writing, and Updating Records
QSAM Return Codes Service Descriptions

QSAM Basics

Using QSAM you can access three types of data sets. These are described in the sections that follow.

Physical Sequential

Defined using DSORG=PS, physical sequential data sets are used for storing data in the order in which it was written. There are no keys, and an individual record can be accessed only by reading past the records that precede it. The records in a physical sequential data set can be fixed or variable length, and they may be blocked or unblocked.

A physical sequential data set's records can be updated in place, and can be extended (records added to the end), if the data set is allocated DISP=MOD.

Physical sequential data sets may reside on direct access devices or on tape.

Partitioned/Partitioned Extended

A partitioned data set (DSORG=PO) is similar to a physical sequential data set, however, the data is partitioned into discrete segments called members. A portion of a PDS's space is reserved for a directory containing information that is used by the operating system to locate a member's storage4. A member's location is recorded in TTR format, where "TT" is 2 bytes identifying the track and "R" is a single byte identifying the block.

When a member is added to a PDS, an entry for the member is inserted into the directory. Entries in the directory are ordered by member name in ascending sequence. Only one user at a time can add a member to a PDS.

A member's records can be updated in place. However, the only way to extend a member is to completely rewrite it (i.e., DISP=MOD will not cause records to be added to the end of a member as it does with sequential files).

When a member is deleted from a partitioned data set, its entry is removed from the directory. However, the space previously occupied by the member's records cannot be reused until the data set is "compressed" by a utility such as IEBCOPY.

PDSE data sets, from a usage standpoint, are identical to partitioned data sets but with additional capabilities. Most importantly, PDSEs permit multiple users to write to a data set simultaneously (though each user must be writing a different member). PDSEs also reuse storage that is freed by member deletions, and thus, do not require periodic compression.

When using QSAM to read a partitioned data set member, you must allocate the data set including the name of the member. If you allocate a PDS without specifying a member name, you will read the data set's directory. Thus, to read all of the members in a data set using QSAM, you must allocate, open, read, close, and free each member individually (a computationally expensive proposition). Because this is a common operation, REXXTOOLS provides another method for processing PDSes, the BPAM interface (see "BPAM Functions" ).

Partitioned and partitioned extended data sets can reside only on DASD. In addition, PDSEs must be managed by DFSMS.

SYSIN/SYSOUT

The Job Entry Subsystem (JES) provides 2 types of data set that can be read or written using QSAM. A SYSIN data set is created when instream data is specified. For example:
//DDIN  DD *
This is a sysin record
This is too
This is the last one
/*
SYSIN data sets have a logical record length of 80 bytes. The data set is terminated with a "/*" or another JCL statement. SYSIN data sets cannot be written. They can be read only.

SYSOUT data sets are created by specification of the SYSOUT keyword on either a DD or ALLOCATE statement. For example:

//DDOUT DD SYSOUT=*,RECFM=VB,LRECL=133
SYSOUT data sets can be written only.

Note: It may be necessary to specify DCB characteristics for SYSIN, SYSOUT, and temporary data sets in order to open them using the REXXTOOLS QSAM interface.

Record Formats

The records in sequential, partitioned, and SYSIN/SYSOUT data sets can take several forms:

The REXXTOOLS QSAM functions support the following formats (RECFMs):

F FA FB FBA
FBM FBS FBSA FBSM
FM FS FSA FSM
V VA VB VBA
VBM VM U UA
UM

The individual letters of RECFM indicate the following:

F
Fixed length records.
V
Variable length records.
U
Undefined length records. From a logical record perspective, U format is similar to variable length records, but the records cannot be blocked.
B
Blocked records.
S
Standard blocks (i.e., all blocks except the last must be the same length)
A
ANSI printer control characters are in the first byte of each record.
M
Machine printer control characters are in the first byte of each record.

For information regarding the selection of an appropriate data set organization and record format refer to the IBM publication Using Data Sets for your system's level of DFP or DFSMS.

Programming For QSAM

The basic flow of a program that processes a data set using QSAM is as follows:
  1. Allocate the file. A Data Definition Name (DDNAME) is associated with the data set you wish to process. Use a status of NEW, OLD, or MOD if you are writing or updating a data set. Use a status of SHR if you are reading a data set. A status of MOD lets you to add new records to the end of a sequential data set (but not a PDS member).
  2. Open the file for processing using the OPEN function. The access method makes a logical connection between your program and the data set to be accessed. If you are reading the file use the INPUT option on OPEN. If you are writing the file, use the OUTPUT option. If you are updating records, use the UPDATE option.
  3. Read a record from the file using GET or write a record to the file using PUT. When reading, the GET function returns the record's contents. You can assign the record to a REXX variable (or variables if you use PARSE VALUE). If the data set is blocked, the access method handles deblocking. Your program processes logical records only (not physical records). A return code of 8 indicates that there are no more records to be read. When writing records, the PUT function accepts the record to be written as an argument. PUT handles any blocking that may be required, and writes physical blocks to the file as required.
  4. If you are reading records, determine what processing applies to the record (if any) and perform the processing. If the data set was opened for update, the record can be replaced. Record deletion is not possible. To delete a record, the entire data set (or PDS member) must be rewritten. For information regarding the processing of record fields, please see "Working with Record Fields"
  5. If appropriate, return to step 3 to process other records.
  6. Close the file using the CLOSE function. Any pending physical writes are completed, and the logical connection between the program and the data set is terminated.
  7. Free the file. The DDNAME is disassociated with the data set.

Allocating Data Sets

The JCL Data Definition (DD) statement may be used to allocate the data set. If dynamic allocation is required, the REXXTOOLS ALLOCATE and FREE commands can be used.

Note: To process a PDS/PDSE member, you must specify the member name on the allocation. If you allocate just the PDS data set name, your program will read the directory.

Opening and Closing Data Sets

To associate your program with the data set you want to process, you use the OPEN function. The CLOSE function performs the opposite action by disassociating your program with the data set.

Shadow REXXTOOLS maintains information about open QSAM files in a data structure associated with the task under which the OPEN function was executed. The information is maintained by ddname. Files remain registered with REXXTOOLS and open until they are explicitly closed, or until the task that opened them terminates.

All REXX programs under a MVS task share the same REXXTOOLS QSAM data structures. Thus if program A calls program B (REXX CALL or function call), any ddname that is opened by either program A or program B is known to the other. File sharing also extends to directly subtasked REXX programs. As a consequence, if Program A attaches program B, the files opened by program A are known by program B. However, files opened by program B will not be known to A when control is returned. This is because task termination will close all files opened by program B.

Notes:

  1. A PDS supports only one writer at a time. If you need to support multiple simultaneous writers, you must use a PDSE.

OPEN Options

When opening a data set using QSAM, you may specify the type of access you want and Data Control Block (DCB) parameters.

Type of Access

The REXXTOOLS QSAM interface supports 3 access options:
INPUT
indicates that you will be reading records from the data set. Data sets that are open for input can be accessed with the GET function only. The data set can be allocated with a status of OLD or SHR (preferred).

VERY IMPORTANT: It is possible to open a newly allocated data set for input. The operation of QSAM in this case is undefined. You may successfully read what appears to be valid data, or (and this is more likely) your program may encounter errors and possibly abnormally terminate.

OUTPUT
indicates that you will writing records to the data set. Data sets that are open for output can be accessed with the PUT function only. The data set should be allocated DISP=OLD.
UPDATE
indicates that you will be reading the data set, and that you may (or may not) be re-writing some or all records. A record must be read before it can be re-written. When open for update, a data set can be accessed with the GET and PUT functions. The data set should be allocated DISP=OLD.

DCB Parameters

Record format (RECFM), logical record length (LRECL), block size (BLKSIZE), and number of buffers (BUFNO) can be specified on OPEN. However, with rare exception, you are much better off letting the operating system derive these values for you. For new data sets, the information can be derived from allocation information that is kept in memory. For existing data sets, the information is taken from the Volume Table of Contents (VTOC).

Reading, Writing, and Updating Records

The Shadow REXXTOOLS QSAM functions for access to records are:
GET
The GET function is used to retrieve records sequentially. GET returns a logical record as its value (or a null string if an error was encountered).
PUT
The PUT function is used to sequentially write or re-write records. PUT accepts a record as one of its arguments. If the PUT follows a GET where the file has been opened for update, the record that was retrieved by the GET will be replaced. In all other cases, PUT is interpreted as a request to add a new record.

If the data set is allocated with a status of NEW, records will be added to the data set starting at the beginning of the data set's space (or the first available space in the case of a PDS or PDSE member). If the data set is allocated with a status of OLD, the records will replace existing records, if any. If a sequential data set is allocated with a status of MOD, records will be appended to the end of the data set. A status of MOD will not cause additional records to be appended to the end of a PDS or PDSE member.

Input Processing

Using the INPUT option of OPEN, you can read, sequentially, some or all of a data set's records. In the following example, a PDS member is read:
/* REXX */
address rexxtool
"alloc fi(indd) da(user.data(timecard)) shr"
if open('qsam','indd','input') <> 0 then do
  say 'OPEN failed with RC='rc 'REASON='reason
  exit 8
end
timerec = get('indd')
do while rc = 0
  parse var timerec lname +10 fname +10 timein +8 timeout +8
  timerec = get('indd')
end
call close 'indd'
"free fi(indd)"

Output Processing

Using the OUTPUT option of OPEN, you can create a data set, or add records to the end of a data set. In the following example, a sequential data set is created:
/* REXX */
address rexxtool
"alloc fi(outdd) da(data.out) new sp(1 1) track",
  "dsorg(ps) recfm(f b) lrecl(80) unit(sysda)"
call open 'qsam', 'outdd', 'output'
parse pull record
do while record <> ''
  call put 'outdd', record
  parse pull record
end
call close 'outdd'
"free fi(outdd)"

Update Processing

Using the UPDATE option of OPEN, you can replace some or all of a data set's records. The interface ensures that replacement records are the same size as the original record. If the replacement record is smaller than the original, the record is padded on the right with blanks. If it is larger than the original record, the replacement record is truncated on the right. No error indication is given in either case.

In the following example, a sequential data set's records are updated. A 2-byte packed decimal date field in YY format is converted to a 2-byte binary date that contains the century.

/* REXX */
if open('qsam','iodd','update') <> 0 then do
  say 'OPEN failed with RC='rc 'REASON='reason
  exit 8
end
record = get('iodd')
do while rc = 0
  parse var record firstpart +25 date +2 lastpart
  date = d2c(p2d(date)+1900,2)
  if put('iodd',firstpart||date||lastpart) <> 0 then do
    say 'PUT failed with RC='rc 'REASON='reason
    exit rc
  end
  record = get('iodd')
end
if close('iodd') <> 0 then
  say 'CLOSE failed with RC='rc 'REASON='reason
exit
Note: For this example, the IODD ddname is assumed to be allocated in the JCL prior to execution. P2D is a REXXTOOLS function for converting packed decimal data to REXX decimal.

QSAM Return Codes

Upon completion, all Shadow REXXTOOLS QSAM functions provide a return code and a reason code. The return code is placed in the standard RC variable, while the reason code is placed in a special variable named REASON.

In addition, except for the GET function which returns a record, all QSAM functions return the return code as their value. For example, the following code will display the return code 2 times, once as OPEN's returned value, and once as the value of the RC variable

say "RC="open('qsam','indd','input') "RC="rc "REASON="reason
Unless otherwise noted, the values for RC and REASON are taken directly from the underlying QSAM macro return and reason codes. In all cases, a return code of zero indicates success.

In the case of GET and PUT, the underlying QSAM macros do not produce return codes. The REXXTOOLS GET function returns RC=8 when end-of-file is reached. All other non-zero GET and PUT return codes are ABEND codes and are accompanied by an "IEC" message.

Very Important: The complete list of QSAM return and reason codes can be found in the IBM publication Macro Instructions for Data Sets for your system's level of DFP or DFSMS. ABEND codes and "IEC" messages are documented in one or more MVS messages and code publications.

Note: The return and reason codes (including ABEND codes) produced by the QSAM functions are all decimal (not hexadecimal) values.

Service Descriptions

The sections that follow describe the syntax and operation of the QSAM-related functions.

CLOSE

GET

OPEN

PUT


© Copyright 1998 by Open Software Technologies, Inc.