You are on page 1of 119

PL/I

23/01/2006

PL/I

Page:- 1 / 119

PL/I

SECTION NO.

23/01/2006

CONTENTS
TOPIC

Page:- 2 / 119

PAGE

SECTION 1

BASIC STRUCTURE OF PL/I PROGRAMS

SECTION 2

COMPILING UNDER MVS

SECTION 3

DATA TYPES AND DATA MANIPULATION

12

SECTION 4

PROCEDURES, FUNCTIONS

21

SECTION 5

SUBROUTINES AND FUNCTIONS

32

SECTION 6

CONTROL STATEMENTS

44

SECTION 7

CONDITIONS AND ON UNITS

47

SECTION 8

ARRAYS

60

SECTION 9

STRUCTURES AND PICTURES

63

SECTION 10

STORAGE CONTROL

69

SECTION 11

FILE PROCESSING GENERAL

82

SECTION 12

STREAM ORIENTED DATA TRANSMISSION

85

SECTION 13

RECORD I/O

93

SECTION 14

DEFINING AND USING VSAM DATA SETS

96

SECTION 15

PL/I AND DB2

102

PL/I

23/01/2006

SECTION 1

Page:- 3 / 119

BASIC STRUCTURE OF PL/I PROGRAMS

back

Simplest Program Skeleton


MYPROG:PROCEDURE OPTIONS(MAIN);
.
.
/* this is a comment */
.
END MYPROG;
Note:1. MYPROG: is an identifier ( otherwise known as Label, Data name, function name etc
in the non PL/I context)
2. Comments are encased in /*
*/
3. OPTIONS(MAIN) indicates entry point of program and must be indicated only for one
procedure in the program.
4. Columns start at 2 and end at 72.
5. Free form of writing
6. Column 2 to 72 user source code
7. Column 73 to 80 may contain program ID or sequence code. It is not checked by the
compiler.
8. Column 1 controls listing like skip to next page, double space etc(ASA Character)
A first simple program
CONPL01: PROC OPTIONS(MAIN);
DCL BUFFER CHAR(80) INIT('HELLO FROM CONSPL01');
PUT SKIP LIST(BUFFER);
GET LIST(BUFFER);
PUT SKIP LIST(BUFFER);
END CONPL01;
PL/I on IBM systems may be either OS PL/I or PL/I for MVS and VM
See the system for the IEL1CL procedure (PL/I for MVS and VM) and IBMZ( OS PL/I)
and the JCL to prepare and run the above program.
Character Sets
$@#
ABCDEFGHIJKLMNOPQRDSTUVWXYZ
0123456789
blank
=
Equal / assignment symbol
+
Plus sign
Minus sign
*
Asterisk / multiply symbol
/
Slash / divide symbol
( )
Parenthesis
,
Comma
.
Point or period
'
Single quotation mark or apostrophe
%
Percent symbol
;
Semicolon

PL/I

23/01/2006
:
&
|
>
<
_
?

Page:- 4 / 119

Colon
NOT symbol
AND symbol
OR symbol
Greater than symbol
Less than symbol
Break (underscore)
Question mark

A combination of symbols may be used , e.g.


A**3 A power 3
A >= A greater than or equal to 3
'A' || 'B' A concatenated with B
IDENTIFIERS
Variable names, Procedure names, Statement Labels, File names etc.
Consists of 1 to 31 alphabetic characters made up of (A - Z, ! , # , $ , 0 - 9, _).
First character must be a Alphabet (A - Z, # , @ , $ or _).
You can use upper or lower case. In identifiers and key words lower case is folded to
upper case.
Example
MY_LABEL_1
CLIENT_NAME
A procedure with OPTIONS(MAIN) is an external procedure . External procedure names
can be maximum of seven characters only. This is also true of a file name.
KEYWORDS
Identifiers that have special meaning to PL/I like GET, PUT etc
STATEMENT FORMAT
LABEL: KEYWORD STATEMENT OPTIONS;
Example
READ_STMT:GET LIST(A,B,C);
Free form statement which can contain as many blanks as felt necessary by programmer.
Position 1 reserved for use by O/S.
Position 2 to 72 may contain one or more PL/I statement each separated by ' ; '
GET LIST (A,B); GET LIST (C,D);
Alternately one statement may be continued across several lines e.g.;
GET LIST
(A,B);
Position 73 to 80 may contain sequence number maintained by editor.

PL/I

23/01/2006
PL/I CONSTANTS
Decimal Fixed Point

Page:- 5 / 119

12.30 , 180, +10.23 , -1.12, 0.03


Maximum precision is 15

Decimal Floating Point

0.1234E+2
X.XXXXXE+XX, X.XXXXXE-XX
Maximum Precision is 33

Character String

'ABCDE'
'ABCD''EF', (2)'ABCD'
Maximum length is 32767

Bit String Constant

'11011'B, (16)'0'B
Maximum length is 32767

Binary Fixed Point

1011B

Binary Floating Point

0.11011011E+48B
Maximum Precision is 109

LIST DIRECTED I/O


INPUT
GET LIST (A,B,C,D,E);
GET FILE(SYSIN) LIST (A,B,C,D,E);

/*Reads from SYSIN


/*explicit form

*/
*/

100 90 80 70 90
OR

90
70
80
90

100
OR
100,90,80,70,90
In List Directed I/O no record boundaries are considered. Items are either separated by a
blank or a comma
GET LIST (A,B) COPY; /* additionally copies the data to SYSPRINT */
Example of Input 12.90,.04E+3,'ABCD','1010'B
Example
DECLARE DATA1
CHAR(12);
GET LIST (DATA1);
OUTPUT
PUT LIST (50,'ABC',123,127);
PUT LIST (23,86,87);
1
50
86

25
ABC
87

49
123

73
127

97
23

121

PL/I

23/01/2006

PUT LIST (A,5,C*D);


PUT PAGE LIST ('ABC');
PUT SKIP LIST(123);
PUT SKIP(0) LIST (123);
PUT SKIP(2) LIST (123);
PUT PAGE LINE (10) LIST (123);
PUT PAGE LIST ((100)'-');

Page:- 6 / 119

/* note use of an expression C*D


*/
/* ABC prints in next page
*/
/* Skip one line before print
*/
/* Print without spacing
*/
/* Skip two lines before print
*/
/* Skip to line 10 of new page and print */
/* new page and 100 dashes
*/

PROGRAM STRUCTURE
MNPROC: PROCEDURE OPTIONS(MAIN);
.
.
CALL SUBPROC_1;
CALL SUBPROC_2;
.
RETURN;
*PROCESS;
SUBPROC_1: PROCEDURE;
.
.
END SUBPROC_1;
*PROCESS;
SUBPROC_2: PROCEDURE;
.
.
END SUBPROC_2;
END MNPROC;

Example of simple nested procedure


//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD1 FIXED DECIMAL(7,2);
DCL FIELD2 FIXED DECIMAL(7,2);
GET DATA (FIELD1,FIELD2);
PUT SKIP LIST(SUM(FIELD1,FIELD2));
SUM:PROCEDURE (A,B) RETURNS(FIXED DECIMAL(7,2));
DCL A FIXED DECIMAL(7,2);
DCL B FIXED DECIMAL(7,2);
RETURN (A+B);
END SUM;
END MYPROG;
/*
//GO.SYSIN DD *
FIELD2=1.23,FIELD1=3.45;
/*
//
Example to demonstrate scope of variables
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M

PL/I

23/01/2006
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD1 FIXED DECIMAL(7,2);
DCL FIELD2 FIXED DECIMAL(7,2);
DCL SUM FIXED DECIMAL(7,2);
GET DATA (FIELD1,FIELD2);
CALL ADD;
PUT SKIP LIST(SUM);
PUT SKIP DATA;
/* Procedure ADD */
ADD:PROCEDURE;
SUM=A+B;
END ADD;
/* End of Procedure ADD */
END MYPROG;
/*
//GO.SYSIN DD *
FIELD2=1.23,FIELD1=3.45;
/*
//

Page:- 7 / 119

PL/I

23/01/2006

SECTION 2

Page:- 8 / 119

COMPILING UNDER MVS

back

See the Catalogued Procedures below in the system.


1.
2.
3.
4.

IEL1C
IEL1CL
IBMZC
IBMZCL

Compile only (PL/I for MVS and VM)


Compile and Link Edit (PL/I for MVS and VM)
Compile only (ZOS PL/I)
Compile and Link (ZOS PL/I)

DD names (PL/I for MVS and VM)


PLI.SYSIN
SOURCE
PLI.SYSLIB
COPY BOOKS
PLI.STEPLIB
LOADLIB WHERE COMPILER RESIDES
PLI.SYSPRINT
COMPILATION LISTING
PLI.SYSLIN
COMPILED OBJECT CODE
PLI.SYSUT1
COMPILER WORK FILE
LKED.STEPLIB
LOADLIB WHERE LINKAGE EDITOR RESIDES
LKED.SYSLIN
PRIMARY INPUT FOR LINKAGE EDITOR
LKED.SYSLMOD
OUTPUT OF LINKAGE EDITOR, THE LOAD MODULE
LKED.SYSIN
SECONDARY INPUT FOR LINKAGE EDITOR
LKED.SYSLIB
LINK LIBRARY OF PRECOMPILED OBJECT CODE
LKED.SYSUT1
LINKAGE EDITOR WORK FILE
LKED.SYSPRINT
LINKAGE EDITOR LISTING
INVOCATION WITH INLINE SOURCE
//MYPROG
JOB
//STEP1
EXEC IEL1CLG,REGION.PLI=1M
//PLI.SYSLIB DD
DSN=MY.SOURCE.LIB,DISP=SHR
//PLI.SYSIN
DD
*
.
Put your source here
.
/*
//
INVOCATION WITH SOURCE IN A PDS MEMBER
//MYPROG
JOB
//STEP1
EXEC IEL1CLG,REGION.PLI=1M
//PLI.SYSLIB DD
DSN=MY.SOURCE.LIB,DISP=SHR
//PLI.SYSIN
DD
DSN=MY.SOURCE.LIB(PROG1),DISP=SHR
//
EXAMPLE
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD CHAR(20) INIT('HELLO WORLD');
PUT LIST (FIELD);
END MYPROG;
//
EXAMPLE OF SEPARATE COMPILATIONS IN ONE JOB
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA

PL/I

23/01/2006

Page:- 9 / 119

//STEP1 EXEC PROC=IEL1C,REGION.PLI=1M


//PLI.SYSIN DD *
SUM: PROCEDURE(A,B) RETURNS(FIXED DEC(3));
DCL (A,B) FIXED DEC(3);
RETURN(A+B);
END SUM;
/*
//STEP2 EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL SUM ENTRY(FIXED DEC(3),FIXED DEC(3)) RETURNS(FIXED DEC(3));
DCL (A,B) FIXED DEC(3);
A=2;B=3;
PUT LIST(SUM(A,B));
END MYPROG;
//
EXAMPLE OF SEPARATE COMPILATIONS IN SEPARATE JOBS(PART A)
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1C
//PLI.SYSIN DD *
SUM: PROCEDURE (A,B);
DCL (A,B) FIXED DECIMAL(6,2);
DCL RESULT FIXED DECIMAL(6,2) EXTERNAL;
RESULT=A+B;
END SUM;
/*
//PLI.SYSLIN DD DSN=USERAA.PLICLASS.OBJ(SUM),DISP=(NEW,KEEP),
// UNIT=SYSDA,SPACE=(TRK,(1,,1))
//
(PART B)
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL SUM ENTRY(FIXED DECIMAL(6,2),FIXED DECIMAL(6,2));
DCL RESULT FIXED DECIMAL(6,2) EXTERNAL;
DCL X FIXED DECIMAL(6,2);
DCL Y FIXED DECIMAL(6,2);
X=123.45;
Y=111.11;
CALL SUM(X,Y);
PUT SKIP EDIT('RESULT IS:',RESULT)(A,F(6,2));
END MYPROG;
/*
//LKED.SYSLIB DD DSN=USERAA.PLICLASS.OBJ(SUM),DISP=OLD
//
DD DSN=CEE.SCEELKED,DISP=SHR
//
EXAMPLE OF SEPARATE COMPILATIONS AND CREATION OF A LOAD MODULE
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CL
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL SUM ENTRY(FIXED DECIMAL(6,2),FIXED DECIMAL(6,2));

PL/I

23/01/2006

Page:- 10 / 119

DCL RESULT FIXED DECIMAL(6,2) EXTERNAL;


DCL X FIXED DECIMAL(6,2);
DCL Y FIXED DECIMAL(6,2);
X=123.45;
Y=111.11;
CALL SUM(X,Y);
PUT SKIP EDIT('RESULT IS:',RESULT)(A,F(6,2));
END MYPROG;
/*
//LKED.SYSLIB DD DSN=USERAA.PLICLASS.OBJ(SUM),DISP=OLD
//
DD DSN=CEE.SCEELKED,DISP=SHR
//LKED.SYSLMOD DD DSN=USERAA.ASMCLASS.LOADLIB(PLI252),DISP=OLD
//
EXAMPLE OF RUNNING A PREVIOUSLY CREATED LOAD MODULE
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC
PGM=PLI252
//STEPLIB
DD
DSN=USERAA.ASMCLASS.LOADLIB,DISP=SHR
//SYSPRINT
DD
SYSOUT=*
//CEEDUMP
DD
SYSOUT=*
//SYSUDUMP DD
SYSOUT=*
//
SOME IMPORTANT COMPILER OPTIONS
ESD | NOESD
Listing of external symbol dictionary
FLAG(I)
List all messages
FLAG(W)
List all except information messages
FLAG(E)
list all except warning and information messages
FLAG(S)
list only severe error messages
DECK | NODECK
Deck specifies that the object module is written in card image
form onto SYSPCH
[NO]AGGREGATE
controls listing of lengths of arrays and structures in program
listing
[NO]ATTRIBUTES (FULL | SHORT)
controls listing of all source program identifiers with their
attributes in the program listing. In SHORT un-referenced
identifiers are omitted.
NOT('not character')

Defines new NOT character

[NO]GONUMBER

controls whether the compiler produces code which will give line
number from the source program to be included in run time
messages

[NO]GOSTMT

Statement numbers will be included in run time messages

[NO]OBJECT

controls whether the compiler produces a object module on


SYSLIN. Always let the default OBJECT hold.

[NO]OPTIONS

controls whether the compiler prints the options in effect in the


program listing

[NO]SOURCE

controls whether the compiler lists the source program in the


program listing.

PL/I

23/01/2006

Page:- 11 / 119

[NO]MACRO

MACRO invokes the MACRO pre-processor before the


compilation process. Specify MACRO if you have coded macros
in your source.

[NO]MAP

MAP produces information on data items to locate them in a


dump.

[NO]LIST

The compiler generates the Assembly code generated for every


line of your source program.

[NO]OFFSET

The compiler prints the offset of the object code for every line
relative to the entry point of the procedure. It is an alternative
way of identifying the failing line in your program from run time
error messages.

SYSTEM (MVS|CMS|TSO|CICS..)
Specifies the format in which parameters are passed to the main
PLI procedure (this is system dependent)
[NO]XREF(FULL|SHORT) Controls inclusion of cross reference table in the listing
These options can be set in the source by the
%PROCESS option,option,..;
or
*PROCESS option,option,.;
You can look at the PL/I programmers guide for explanation on all possible options.

PL/I

23/01/2006

SECTION 3

Page:- 12 / 119

DATA TYPES AND DATA MANIPULATION

back

PL/I Data Type


FIXED DECIMAL

IBM Data Format


Packed Decimal

Storage
4 bits / decimal
digit, sign

Basic Usage
Arithmetic
Operations

FIXED BINARY

Fixed Point

Halfword or
Fullword

Arithmetic
Operations

FLOAT DECIMAL
FLOAT BINARY

Floating Point

32 | 64 | 128 Bits
Arithmetic
Operations

PICTURE

Zoned Decimal

8 bits per digit

Numeric
Character

CHARACTER

Character

8 bits per Char

Alphabetic
Character

BIT

Bit

One byte min

Logical
Data

DECLARE PRICE DECIMAL FIXED(5,2);


Precision of five digits of which two
are decimal fraction
Scale Attribute
Base attribute
Identifier (variable)
PL/I Keyword
DECLARE PRICE FLOAT DECIMAL(6);
PRICE = 3.12345
DECLARE PRICE DECIMAL FIXED(6,2);
PRICE = 1234.56;
DECLARE PRICE FIXED BINARY(15); /* 15 bits + sign bit */
PRICE = 123456;
DECLARE PRICE FIXED BINARY(31); /* 31 bits + sign bit */
PRICE = 123456;
DECLARE PRICE FIXED BINARY(15,2);/* 15 bits + sign. implied 2 bits fraction
1111111111111.11 fraction max .75 decimal*/
MODE ATTRIBUTE
DCL COMPLEX_VAR FLOAT DECIMAL (6) COMPLEX;
Both Real and imaginary component have FLOAT DECIMAL (6) base Scale and
Precision.
Partial Declarations possible
DCL A DECIMAL;

/*
only base defined
/* scale and precision default to FLOAT (6)

*/
*/

DCL A BINARY

/*
only base defined
/*scale and precision default to FLOAT(21)

*/
*/

DCL A FIXED;

/*
only scale defined
*/
/* base and precision default to DECIMAL (5,0)*/

DCL A FLOAT /*

only scale defined


*/
/* base and precision default to DECIMAL (6) */

PL/I

23/01/2006

Page:- 13 / 119

Other attributes default to the table below


Declared Attributes
Default Attributes
DECIMAL FIXED
(5,0)
DECIMAL FLOAT
(6)
BINARY FIXED
(15,0)
BINARY FLOAT
(21)
DECIMAL
FLOAT (6)
BINARY
FLOAT (21)
FIXED
DECIMAL (5,0)
FLOAT
DECIMAL (6)
None, Variable begins with I-N
BINARY FIXED (15)
None, Variable begins with A-H,O-Z,@,#,$
DECIMAL FLOAT (6)
Bit Data
DCL END_OF_FILE
BIT(1);
DCL NO
BIT(1) INIT('0'B);
DCL YES
BIT(1) INIT('1'B);
.
.
END_OF_FILE = NO;
ON ENDFILE(SYSIN) END_OF_FILE = YES;
DO WHILE(
END_OF_FILE);
.
.
END;
Character Data
DCL CHAR_DATA
Varying attribute
DCL
CHAR_DATA

CHAR(20) INIT('ABCDEFGH');
/* Left Justified and padded on right with blanks

*/

CHAR(20)
VARYING;
/* This is a two byte length field followed */
/* by a 20 byte field for data
*/

CHAR_DATA = 'MY NAME';


CHAR_DATA ='';
/* null string */
DCL BIT_DATA
BIT(8) VARYING;
Defined attribute
DCL
A
CHAR(20);
DCL
B
CHAR(5) DEF A;
DCL
C
CHAR(10) DEF A;
DCL
D
CHAR(10) DEF C;
Permitted Redefines
Base Identifier
Coded Arithmetic variable

/* overlay on A
*/
/* overlay on A
*/
/* ERROR !, not allowed

*/

Character String
Bit String

Defined Item
coded arithmetic variable with same BASE, SCALE,
PRECISION
Character string or Bit String
Character string or Bit string

Position Attribute
DCL
CHAR_LIST
DCL
A

CHAR(20);
CHAR(10) DEFINED CHAR_LIST;

PL/I

23/01/2006

DCL

Page:- 14 / 119

/* overlays first ten positions


*/
CHAR(10) DEFINED CHAR_LIST POSITION(10);
/* overlays next 10 positions
*/

Initial Attribute
Use this to initialise variables
DCL
A
FIXED DECIMAL (7,2) INITIAL(24.50);
DCL
B
CHAR(10) INIT((10)'A');
DCL
C
BIT(2) INIT('00'B);
DCL
D
FIXED BINARY(15) INIT (0);
same effect can also be achieved by an assignment statement at lower code efficiency.
PL/I Data Attributes
FIXED DECIMAL ( Also represented as FIXED, DECIMAL FIXED)
Data Format
Packed Decimal
Type of Data
Coded arithmetic
Default Precision
5 Decimal Digits
S99,999
Maximum Precision
15 Decimal digits
S999,999,999,999,999
Used for monetary calculations
Example:
DCL
A
FIXED DECIMAL (5,2) INIT (123.45);
FIXED BINARY
Data Format
Fixed point signed binary (Halfword or Fullword)
Type of Data
Coded arithmetic
Default Precision
15 Bits plus sign bit
(Decimal 32,767)
Maximum Precision
31 Bits plus sign bit
(Decimal 2,147,483,647)
Used for fast computations, usually for integers
Variables beginning I-N default to fixed Binary unless explicitly defined otherwise
Can be used for representing fractions, but fractions do not have exact representation
in Binary .
Example:
DCL
A
FIXED BINARY (31) INIT(2147483647);
FLOAT DECIMAL (Also represented as DECIMAL)
Data Format
Floating Point, 32bit, 64 bit or 128 bit representation
Type of data
Coded Arithmetic
Default precision
6 Decimal digits
Maximum precision
33 Decimal digits
Examples:
DCL
A
FLOAT DEC(6);
DCL
A
DEC (6);
/* Defaults to FLOAT */
DCL
A
FLOAT DEC(6) INIT(6.0E+12);
Note that in storage there is no difference between FLOAT BINARY and FLOAT
DECIMAL data representation. They are both represented as E, D and L format floating
point data, depending on the precision.
BIT
Data Format
bit (8 bits per byte)
Type of Data
Logical
Default length
None
Maximum Length
32767 bits
Examples
DCL
FLAGS BIT(8)
INIT('11100001'B);
DCL
FLAGS BIT(32)
INIT((32)'0'B);
Note that bit strings are (like character strings ) assigned left to right. If a smaller bit string
is assigned to a larger string, padding with binary zeroes takes place on the vacant right

PL/I

23/01/2006

Page:- 15 / 119

positions. If the assigned bit string is larger than the receiving field then truncation takes
place on the right.
CHARACTER
Data Format
Character
Type of data
Alphanumeric (anything goes)
Default length
None
Maximum Length
32767 characters
On assignment data is left aligned and vacant positions on right are padded with spaces.
If the receiving field is smaller, then truncation takes place on the right.
Example:
DCL
A
CHAR(20) INIT('ABCDEF');
DCL
A
CHAR(10) INIT((10)' ');
DATA DECLARATION
Explicit Declaration
The scope of an explicit declaration of a name is that block to which the declaration is
internal, including all contained blocks, except those blocks (and any blocks contained
within them) to which another explicit declaration of the same name is internal.
A block in PL/I can be a Procedure block or Begin block. Begin blocks are delimited by
BEGIN and END keywords.
P: PROC;
DCL A, B;
Q: PROC;
DCL B, C;
R: PROC
DCL C, D

/* b of proc p is now hidden by this b */


/* a of proc p is still visible
*/
/* c of proc q is visible
/*
/*
/*
/*

*/

c of proc q is now hidden by this c */


a of proc p is still visible
*/
b of proc q is still visible
*/
d of proc r is visible
*/

END R;
/* now c of proc q is visible
/* b of proc q is visible
/* a of proc p is visible

*/
*/
*/

/* Now B of PROC P is visible


/* A of PROC P is visible

*/

END Q;
*/

END P;
DECLARE Statement
The DECLARE statement specifies attributes of a name and its position determines the
scope of the declaration of the name.
DECLARE [level] name [attribute] , [level] name [attribute]
Factoring of Attributes
Attributes common to several names can be factored to eliminate repeated specification
of the same attribute.
DECLARE (A,B,C,D) BINARY FIXED (31);

PL/I

23/01/2006

Page:- 16 / 119

DECLARE (E DECIMAL(6,5), F CHARACTER(10)) STATIC;


DECLARE ((A,B) FIXED(10),C FLOAT(5)) EXTERNAL;
Implicit Declaration of Names
If a name appears in a program and is not explicitly declared, it is implicitly declared.
PROC1: PROC;
/*implicit declaration takes place here in the outer most containing block*/
PROC1A;
X=4;
/* Not declared explicitly, so an implicit declaration takes place*/
END PROC1A;
END PROC1;
Implicit declaration takes place in the outer most containing block ( external procedure).
Consequently, the name is known throughout the entire external procedure, except for
any blocks in which the name is explicitly declared.
If the attributes for a name declared implicitly can be determined from the context in
which the name appears, then the compiler uses these. These are known as contextual
declarations. For example,
A name that appears in a CALL statement, in a CALL option, or is followed by an
argument list is given the BUILTIN and INTERNAL attributes.
A name that appears in a FILE or COPY option, or a name that appears in an ON,
SIGNAL statement for a condition that requires a file name is given a FILE attribute.
(You will get a detailed explanation of FILE attribute in the section on I/O).

A name that appears in an ON CONDITION or SIGNAL CONDITION statement is


given the CONDITION attribute. (You will get a detailed explanation of CONDITION
attribute in the section on ONUNITS).

A name that appears in the BASED attribute, in a SET option, or on the left-hand side
of a locator qualification symbol is given the POINTER attribute. (You will get a
detailed explanation of BASED and POINTER attribute in the section on Storage
Management).

A name that appears in an IN option, or in the OFFSET attribute, is given the AREA
attribute. (You will get a detailed explanation of BASED and POINTER attribute in the
section on Storage Management).
Examples of contextual declaration are:
READ FILE (ABC) INTO (Q);
ALLOCATE X IN (S);
In these statements, ABC is given the FILE attribute, and S is given the AREA attribute.

PL/I

23/01/2006

Page:- 17 / 119

Scopes of Declarations
P

A: PROCEDURE;
DECLARE P, Q;
B: PROCEDURE;
DECLARE Q;
R = Q;
C: BEGIN;
DECLARE R;
DO I = 1 TO 10;
.
END;
END C;
END B;
D: PROCEDURE;
DECLARE S;
END D;
END A;
P is declared in the block A and known throughout A since it is not re-declared.
Q is declared in block A, and re-declared in block B. The scope of the first declaration
of Q is all of A except B; the scope of the second declaration of Q is block B only.
R is declared in block C, but a reference to R is also made in block B. The reference
to R in block B results in an implicit declaration of R in A, the external procedure.
Therefore, two separate names (R and R`) with different scopes exist. The scope of
the explicitly declared R is block C; the scope of the implicitly declared R is all of A
except block C.
I is referred to in block C. This results in an implicit declaration in the external
procedure A. As a result, this declaration applies to all of A, including the contained
procedures B, C, and D.
S is explicitly declared in procedure D an is known only within D.
INTERNAL and EXTERNAL Attributes
INTERNAL specifies that the name can be known only in the declaring block. Any other
explicit declaration of that name refers to a new object with a different, non overlapping
scope.
A name with the EXTERNAL attribute can be declared more than once, either in different
external procedures or within blocks contained in external procedures. All declarations of
the same name with the EXTERNAL attribute refer to the same data.
EXTERNAL is the default for controlled variables, file constants, entry constants,
Because external declarations for the same name all refer to the same data, they must all
result in the same set of attributes.
Language Specified Defaults

PL/I

23/01/2006

Page:- 18 / 119

When a problem-data name has not been declared with a data type or when the
RETURNS option is omitted from a function procedure, the default is coded arithmetic
problem data.
If mode, scale, and base are not specified by a DECLARE or DEFAULT statement, or by
a RETURNS option, variables with names beginning with any of the letters I through N
are given the attributes REAL FIXED BINARY (15,0), and those with names beginning
with any other alphabetic character are given the attributes REAL FLOAT DECIMAL (6).
If mode, scale, or base is specified by a DECLARE or DEFAULT statement, or by a
RETURNS option, the remaining attributes are completed from the following list of
defaults:
The default base is DECIMAL.
The default scale is FLOAT.
The default mode is REAL.
Default precision is then completed from the following list:
(5,0) for DECIMAL FIXED
(15,0) for BINARY FIXED
(6) for DECIMAL FLOAT
(21) for BINARY FLOAT
For example the statement:
DCL X BINARY (15)
/* no scale specified .gets the attributes REAL and FLOAT

*/

DCL X BINARY (15,0)


/* scale specified gets the attributes REAL and FIXED.

*/

DEFAULT Statement
The DEFAULT statement specifies data-attribute defaults (when attribute sets are not
complete). Any attributes not applied by the DEFAULT statement for any partially
complete explicit or contextual declarations, and for implicit declarations, are supplied by
language-specified defaults.
Abbreviation: DFT RANGE(letter)
Specifies that the defaults apply to names that begin with the name(s) specified. Letter
can be any letter in the English alphabet.
Example:
RANGE (ABC)
applies to these names:
ABC
ABCD
ABCDE
but not to:
ABD
ACB
AB
A
Hence a single letter in the range-specification applies to all names that start with that
letter.

PL/I

23/01/2006

Page:- 19 / 119

RANGE (letter : letter)


Specifies that the defaults apply to names with initial letters that either correspond to the
two letters specified, or to any letters between the two in alphabetic sequence.
RANGE(*)
Specifies all names in the scope of the DEFAULT statement.
Example:
DEFAULT RANGE (A:C) VALUE (FIXED DEC(10),FLOAT DEC(14),AREA(2000));
DECLARE B FIXED DECIMAL, C FLOAT DECIMAL, A AREA;
These statements are equivalent to:
DECLARE B FIXED DECIMAL (10), C FLOAT DECIMAL(14), A AREA(2000);
Example:
DFT RANGE(I:N) FIXED BINARY VALUE(FIXED BINARY(15));
DFT RANGE(A:H,O:Z) FLOAT DECIMAL VALUE(FLOAT DECIMAL(6));
Data Conversions
When mixed data types appear in an arithmetic expression, PL/I causes the following
conversions to take place to make the expression have all tokens of the same data type.
1. If base of the data item differs, DECIMAL is converted to BINARY
2. If the scale differs FIXED is converted to FLOAT
3. If CHARACTER and BIT are involved, then BIT is converted to CHARACTER a Bit 1
becoming character 1, a zero becoming character 0.
Values operated on
DCL A FIXED DEC,
B FLOAT DEC;
Y = A + B;

Conversion
A is converted
to FLOAT

Comments
scale is different
FIXED -> FLOAT

DCL C FIXED DEC,


D FIXED BIN;
I = C*D;

C is converted
to FIXED BIN

base is different
DEC -> BIN

DCL E FIXED DEC,


F FLOAT BIN;
R = E/F;

E is converted
to FLOAT BIN

base and scale are


different
FIXED -> FLOAT
DECIMAL -> BINARY

DCL K CHAR(13),
I CHAR(5),
J BIT(8);
K = I || J;

J is converted to
CHAR(8)

BIT -> CHARACTER

Assignment Statements

PL/I

23/01/2006
DCL
DCL
DCL

A
B
C

Page:- 20 / 119

FIXED DECIMAL(5,2) INIT(1.12);


FIXED DECIMAL(5,2) INIT(2.32);
FIXED DECIMAL(5,2);

C = A+B;
/* note that assignment is the = sign */
A,B,C = 0;
/* all three variables assigned value 0 */
C = A*3 ;
/* Right hand side is an expression */
Note that the assigned term can be a variable or an expression ( of constants /
constants and variables.
Arithmetic Operations
**
Exponentiation
*
Multiplication
/
Division
+
Addition
Subtraction
Rule 1: Order of resolution in an arithmetic expression is Exponentiation first going right
to left. Next moving from left to right multiplication or division, whichever appears
first. Then addition or subtraction , whichever appears first, moving from left to
right.
Rule 2: If parenthesis are used in an expression, the innermost expression within the
parenthesis is resolved first, in consonance with Rule 1, and the resolution
moves outwards. Example A * (B+C)
/* resolve B+C first and multiply the result by A
*/
Rule 3: Prefix operators are performed before infix operators
Example A = B**-A
/* Negate A first. Then raise B to the power of A

*/

Rule 4: Any expression or element may be raised to a power which can be a negative
value Example A**2, (A+5)**3, A**-B
Rule 5: If two or more operators with the highest priority appear in the same expression,
the order of priority is from right to left
Example -A**2 /* Exponentiation first, Negation next
*/
A**B**C /* Evaluate B**C first.Then raise A to the resultant power*/
Operator Precedence
Level
Operator
1
prefix +, prefix -, ** ,
2
*, /
3
infix +, infix 4
||
5
>=,>,=,<, =,..
6
&
7
|

PL/I

23/01/2006

SECTION 4

PROCEDURES, FUNCTIONS

Page:- 21 / 119

back

Built In Functions
Declaration required
DCL
function-name BUILTIN;
Example
DCL
DATE

BUILTIN;

The important built in functions are covered in detail in the next section. Only one
example of the SUBSTR built in function is explained below to introduce the concept of a
PSEUDO variable
Pseudo variables
SUBSTR(arg1,I,J)
Returns a sub-string from arg1 (character, Bit or Picture attribute)starting at position I and
J characters long. Arg1 can be a constant, variable name or an expression. It may also
be specified as a LIST item in a GET or PUT statement.
SUBSTR is a built in function that can appear on the LHS of an assignment statement (or
as a receiving field, for example in a GET statement).
Example:
DCL
DATE CHAR(6) INIT('980215');
SUBSTR(DATE,1,2)='99';
/* change YY to 99
*/
GET LIST (SUBSTR(DATE,3,4)); /* get MMDD from SYSIN
*/
PROGRAM ORGANISATION
Programs
A PL/I program consists of one or more external procedures. Each procedure can
contain other procedures, begin-blocks, or both.
Program Activation
A PL/I program becomes active when a calling program invokes the main procedure.
CONTRL: PROCEDURE OPTIONS(MAIN);
CALL A;
CALL B;
CALL C;
END CONTRL;
Blocks
A block is a delimited sequence of statements that determines the scope of the
names declared within it, limits the allocation of automatic variables, and
determines the scope of DEFAULT statements.
Two kinds of blocks: procedure blocks and begin-blocks.
Begin-blocks and procedures can contain declarations that are treated as local
definitions of names.
These declarations are not known outside their own block.
Automatic storage is allocated upon entry to the block where the storage is
declared. The storage is freed upon exit from the block.

PL/I

23/01/2006

Page:- 22 / 119

Block Activation
Except for the main procedure, external and internal procedures contained in a
program are activated only when they are invoked by a procedure reference.
Begin-blocks are activated through sequential flow or as ON-units.
During Block Activation:
Storage is allocated for automatic variables and initialisation, if specified.
Currently active blocks known to the procedure are identified, so that the correct
generations of automatic storage are accessible, and the correct ON-units can be
entered.
Storage is also allocated at this time for any dummy arguments that might be created
in this block.
Block Termination
A procedure is terminated when control passes back to the invoking block or to
some other active block, by means other than a procedure reference. Similarly, a
begin-block is terminated when control passes to another active block, by means
other than a procedure reference.
During block termination:
The ON-unit environment is re-established as it existed before the block was
activated.

Storage for all automatic variables allocated in the block is released.

Internal and External Blocks


There can be no overlapping of blocks;
A procedure that is contained within another block is called an internal procedure. A
procedure that is not contained within another block is called an external procedure.

PL/I

23/01/2006

Page:- 23 / 119

Begin-blocks are always internal;


A: PROCEDURE;
.
.
B: BEGIN;
.
.
END B;
.
.
C: PROCEDURE;
.
.
D: BEGIN;
.
.
E: PROCEDURE;
.
.
END E;
.
.
END D;
END C;
.
.
END A;
Procedures
A procedure is a sequence of statements delimited by a PROCEDURE statements and a
corresponding END statement.
For example:
NAME: A:

PROCEDURE;
.
.
.

END NAME;
The leftmost label of the PROCEDURE statement represents the primary entry point of
the procedure. Optionally, additional labels define secondary entry points. The ENTRY
statement also defines secondary entry points.
B: ENTRY;
PROCEDURE and ENTRY Statements
A procedure (subroutine or function) can have one or more entry points. The primary
entry point to a procedure is established by the leftmost label of the PROCEDURE
statement. Secondary entry points to a procedure are established by additional labels of
the PROCEDURE statement and by the ENTRY statement.
A: I: PROCEDURE (X);
is the same as:

PL/I

23/01/2006

Page:- 24 / 119

A: PROCEDURE (X);
I : ENTRY (X);
Declaring an External Entry Point
DCL Y EXTERNAL ENTRY;
When an EXTERNAL ENTRY is declared without a parameter descriptor list, matching
between parameters and arguments does not occur. Therefore, no diagnostic message is
issued for any incorrect (Number, Type or sequence)arguments that are specified in a
CALL to such an entry point.
Example:
DECLARE X ENTRY EXTERNAL;
CALL X(parameter);
/* No diagnostic message issued */
Format for PROCEDURE statement
entry-constant: PROCEDURE (parameter, parameter,)
RETURNS(attribute) OPTIONS (Options-list)
Format for ENTRY statement
entry-constant: ENTRY (parameter, parameter,)
RETURNS(attribute) OPTIONS (Options-list)
Important OPTIONS are
BYADDR or BYVALUE
Specify how all parameters defined for this procedure entry are passed. If you specify
BYADDR, parameters are received by address. If you specify BYVALUE, parameters are
received by value. Any change to a parameter that is being passed by value is not
reflected in the argument passed by the caller. BYADDR is the default
BYVALUE can be specified for EXTERNAL PROCEDURE statements, but it cannot be
specified for internal procedures or ENTRY statements. Additionally BYVALUE entry
points can only have scalar arguments and return values that are either POINTER or
REAL FIXED BINARY(31,0)
BYADDR is the default.
Example:
EXTR: PROCEDURE(A,B) OPTIONS (BYADDR);
DCL (A,B) FLOAT;
MAIN

The PL/I procedure is the initial procedure of a PL/I program. The operating
system control program invokes it as the first step in the execution of that
program.

COBOL Specifies that the designated entry point is in a COBOL subprogram.


ASSEMBLER specifies that the designated entry point is in an assembler subroutine.
PL/I passes arguments directly to the subroutine, rather than via PL/I control
blocks. Entries with the ASSEMBLER option are subject to the following rules:

They cannot be used as a function reference.

PL/I

23/01/2006

Page:- 25 / 119

Any number of arguments can be passed in the CALL statement invoking the
entry, from zero up to the number specified by the entry declaration, but
intervening arguments cannot be omitted.

Parameter Attributes
Because a parameter has no associated storage within the invoked procedure, it cannot
be declared to have any of the storage attributes STATIC, AUTOMATIC, BASED, or
DEFINED. However, it can be declared to have the CONTROLLED attribute.
Procedure Activation
After the keyword CALL in a CALL statement.
After the keyword CALL in the CALL option of the INITIAL attribute
Example: SET_UP is the name of a procedure that can set the initial values of
elements in TABLE. X and Y are arguments passed to SET_UP.
DECLARE TABLE (20,20) INITIAL CALL SET_UP (X,Y);
As a function reference
Entry Points
A:

READIN:
PROCEDURE;
statement-1
statement-2
ERRT: ENTRY;
statement-3
statement-4
statement-5
NEXT: RETR: ENTRY;
statement-6
...
END READIN;
In the example, A is the primary entry point. A and READIN specify the same entry point,
as do NEXT and RETR. However note that depending on the entry point used to invoke
the procedure, the code executed can be different. The procedure can be activated by
any of the following statements:
CALL
CALL
CALL
CALL
CALL

A;
ERRT;
NEXT;
RETR;
READIN;

Entry Variables
DECLARE ENT1 ENTRY VARIABLE;
ENT1 = ERRT;
CALL ENT1; /* this call and the next call have the same effect
CALL ERRT; /* this call and the previous have the same effect
Procedure Termination
Normal procedure termination occurs when:

*/
*/

PL/I

23/01/2006

Page:- 26 / 119

Control reaches a RETURN statement within the procedure. The execution of a


RETURN statement returns control to the point of invocation in the invoking
procedure. If the point of invocation is a CALL statement, execution in the invoking
procedure resumes with the statement following the CALL. If the point of invocation is
one of the other forms of procedure references (that is, a CALL option of the INIT
keyword or a function reference), execution of the statement containing the reference
is resumed.
Control reaches the END statement of the procedure. Effectively, this is equivalent to
the execution of a RETURN statement.
Transferring control out of a procedure using a GO TO statement can sometimes result in
the termination of several procedures and/or begin-blocks. Specifically, if the transfer
point specified by the GO TO statement is contained in a block that did not directly
activate the block being terminated, all intervening blocks in the activation sequence are
terminated.
Example:
A: PROCEDURE OPTIONS(MAIN) ;
statement-1
statement-2
B: BEGIN;
statement-b1
statement-b2
CALL C;
statement-b3
END B;
statement-3
statement-4
C: PROCEDURE;
statement-c1
statement-c2
statement-c3
D: BEGIN;
statement-d1
statement-d2
GO TO LAB;
statement-d3
END D;
statement-c4
END C;
statement-5
LAB: statement-6
statement-7
END A;
A activates B, which activates C, which activates D. In D, the statement GO TO LAB
transfers control to statement-6 in A. Since this statement is not contained in D, C, or B,
all three blocks are terminated; A remains active. Thus, the transfer of control out of D
results in the termination of intervening blocks B and C as well as the termination of block
D.
Dynamic Loading of an External Procedure
Use FETCH and RELEASE statements

PL/I

23/01/2006

Page:- 27 / 119

PROGA: PROC OPTIONS(MAIN);


DCL (C,D) FIXED BIN(31);
DCL PROGB ENTRY(FIXED BIN(31),FIXED BIN(31));
FETCH PROGB;
CALL PROGB(C,D);
RELEASE PROGB;
END PROGA;
Example
//
JOB
//DYNSTP EXEC PROC=IEL1CL,REGION.PLI=1M
//PLI.SYSIN DD *
PROGB : PROC(A,B) OPTIONS(FETCHABLE);
DCL (A,B) FIXED BIN(31);
PUT LIST('A AND B:' ,A,B);
END PROGB;
/*
//PLI.SYSLIN DD DSN=&&LOADSET,DISP=(NEW,KEEP),.......same parms as
existing SYSLIN statement in IEL1CL proc
//LKED.OBJMOD DD DSN=&&LOADSET,DISP=(OLD,DELETE)
//LKED.SYSLMOD DD DSN=MYLIB.PLI.LOADLIB,DISP=OLD
//LKED.SYSLIN DD *
ENTRY PROGB
INCLUDE OBJMOD
NAME PROGB( R )
/*
JCL for compile and link edit main proc.
//.. JOB
//COMPLEK EXEC IEL1CL
//PLI.SYSIN DD *
PROGA: PROC OPTIONS(MAIN);
DCL (C,D) FIXED BIN(31);
DCL PROGB ENTRY(FIXED BIN(31),FIXED BIN(31));
C=1;D=2;
FETCH PROGB;
CALL PROGB(C,D);
RELEASE PROGB;
END PROGA;
/*
//LKED.SYSLMOD DD DSN=MYLIB.PLI.LOADLIB(PROGA),DISP=SHR
Executing main proc
//STEP EXEC PGM=PROGA
//STEPLIB DD DSN=MYLIB.PLI.LOADLIB,DISP=SHR
Subroutines
A subroutine is a procedure that is invoked by a CALL statement or CALL option of an
INITIAL attribute. It can be either an external or an internal procedure.
Example of Invocation of subroutines that are internal to and external to the invoking
block.
Example
PRMAIN: PROCEDURE;
DECLARE NAME CHARACTER (20), ITEM BIT (5),
OUTSUB ENTRY;

PL/I

23/01/2006

Page:- 28 / 119

CALL OUTSUB (NAME, ITEM);


END PRMAIN;
OUTSUB: PROCEDURE (A,B);
DECLARE A CHARACTER (20), B BIT (5);
PUT LIST (A,B);
END OUTSUB;
Example
A: PROCEDURE;
.
.
CALL INSUB (. . ., . . ., . ., .);
INSUB: PROCEDURE (W,X,Y,Z) ;
DECLARE W . . .,
X . . .,
Y . . .,
Z . . .;
END INSUB;
END A;
BUILTIN Subroutines
These have entry name that are defined at compile-time and are invoked by a CALL
statement.
Functions
A function is a procedure that is invoked by a function reference in an expression. A
function reference is an entry reference that represents an entry name (a particular entry
point of a procedure) invoked as a function.
A function returns a value, and control, to replace the function reference in the evaluation
of the expression in which the function reference appears. This single value can be of any
data type except entry.
Examples
MAINP:

PROCEDURE;
GET LIST (A, B, C, Y);
X = Y**3+SUBA(A,B,C);

.
.
END MAINP;
SUBA: PROCEDURE (U,V,W) RETURNS (BIN FLOAT (21));
DCL (U,V,W) . . . ;
IF U > V + W
THEN RETURN (0);
ELSE
RETURN ( U*V*W);
END SUBA;
Association of Arguments and Parameters

PL/I

23/01/2006

Page:- 29 / 119

When a function or subroutine is invoked, parameters in the parameter list are associated
, from left to right, with the arguments in the argument list. The number of parameters and
arguments must be the same.
Dummy Arguments
A reference to an argument, not its value, is generally passed to a subroutine or function.
This is known as passing arguments by reference.
However, this is not always possible, for example when constants are passed. Therefore,
the compiler allocates storage (in storage belonging to the invoking procedure) for some
arguments using attributes that agree with the parameter, converts, and assigns the
argument to the allocated storage, and then passes a reference to the allocated storage.
These storage locations are called dummy arguments. Any change to a parameter for
which a dummy argument has been created is reflected only in the value of the dummy
argument and not in the value of the original argument from which it was constructed.
A dummy argument is created when the original argument is any of the following:
A constant.
An expression with operators, parentheses, or function references.
A variable whose data attributes or alignment attributes are different from the
attributes declared for the parameter.
In the case of arguments and parameters with the PICTURE attribute, a dummy
argument is created unless the picture specifications match exactly, after any
repetition factors are applied.
A controlled string or area (because an ALLOCATE statement could change the length
or extent).
Passing an Argument to the MAIN Procedure
TOM: PROC (PARAM) OPTIONS (MAIN) ;
DCL PARAM CHAR(100) VARYING;
When NOEXECOPS is specified, the MAIN procedure can have a single parameter
that is a VARYING CHARACTER string. The parameter is passed as is, and a
descriptor is set up. (/, if contained in the string, is treated as part of the string).
Example:
MAIN: PROC(PARM) OPTIONS(MAIN NOEXECOPS);
DCL PARM CHAR(n) VARYING;
Note that the OPTION NOEXECOPS implies that the PARM string is not evaluated
by the run environment of MVS but passed unchanged to the user program.
Shows a MAIN procedure that can be invoked as follows:
//

EXEC PGM=MAIN, PARM=REPORT,LIST

The PARM contains REPORT,LIST and has a length of 11.


When PL/I programs are called from Assembler routines, more than one parameter
can be passed as below:
Example

PL/I

23/01/2006

Page:- 30 / 119

MAIN: PROC(FUNC, P) OPTIONS(MAIN NOEXECOPS);


DCL FUNC FIXED BIN(31);
DCL P POINTER;
Begin-Blocks
A begin-block is a sequence of statements delimited by a BEGIN statement and a
corresponding END statement.
Example:
B:

BEGIN;
statement-1
statement-2
.
.
.
statement-n

END B;
Unlike a procedure, a label is optional for a begin-block.
Begin-block Activation
Begin-blocks are activated through sequential flow or as a unit in an IF, ON, WHEN, or
OTHERWISE statement. Control can be transferred to a labelled BEGIN statement by
execution of a GO TO statement.
Begin-Block Termination
A begin-block is terminated when control passes to another active block by some means
other than a procedure reference; that is, when:
Control reaches the END statement for the block. When this occurs, control moves to
the statement physically following the END, except when the block is an ON-unit.
The execution of a GO TO statement within the begin-block (or any block activated
from within that begin-block) transfers control to a point not contained within the block.
A STOP or EXIT statement is executed (thereby terminating execution of the current
task and all its sub-tasks).
Control reaches a RETURN statement that transfers control out of the begin-block
(and out of its containing procedure as well).
Entry Data
Entry data can be an entry constant or the value of an entry variable. An entry constant is
a name written as a label prefix to a PROCEDURE or ENTRY statement, or a name
declared with the ENTRY attribute and not the VARIABLE attribute. An entry constant can
be assigned to an entry variable.
Example:
P:

PROCEDURE;
DECLARE EV ENTRY VARIABLE,
(E1,E2) ENTRY;
EV = E1;
CALL EV;
EV = E2;
CALL EV;

PL/I

23/01/2006

Page:- 31 / 119

P, E1, and E2 are entry constants. EV is an entry variable.


Declaring Entry Data
Internal entry constants are explicitly declared by the appearance of a label prefix to a
PROCEDURE or ENTRY statement. A parameter-descriptor list (the number of
parameters and their attributes) is obtained from the parameter declarations, if any, and
by defaults.
you must explicitly declare external entry constants. This declaration:
Defines an entry point to an external procedure
optionally specifies a parameter-descriptor list, if any, for the entry point
Optionally specifies the attributes of the value that is returned by the procedure if the
entry is invoked as a function.
Examples of parameter descriptor list
ENTRY (CHARACTER(10),,,FIXED DEC);
/* 4 parameters */
ENTRY (*);
/* indicates one parameter
*/
ENTRY(FLOAT BINARY, );
/*indicates 2 parameters
*/
ENTRY ( );
/* specifies entry has nil parms*/
ENTRY;
/*Suppresses argument checks*/

BUILTIN Attribute
Specifies that the name is a built-in function name, PSEUDOVARIABLE name, or built-in
subroutine name
A:
PROCEDURE;
DECLARE SQRT FLOAT BINARY;
X = SQRT;
/* programmer declared SQRT is assigned */
B: BEGIN;
DECLARE SQRT BUILTIN;
Z = SQRT(P);
/* internal SQRT function is called*/
END B;
END

A;

RETURN Statement
The RETURN statement terminates execution of the procedure that contains the
RETURN statement.
RETURN (expression);
The RETURN statement with expression is used to terminate a procedure invoked by a
function reference.

PL/I

23/01/2006

Page:- 32 / 119

SECTION 5

SUBROUTINES AND FUNCTIONS


back
Subroutines and Procedures
Main Procedures (entry point into program)
Subroutine Procedures
Function Procedures
Procedures can be nested (unlike C and like Pascal)
Arguments can be passed to procedures
Function procedures can return value (like C functions)
Subroutine procedures return value(s) through modification of argument(s) passed
Invoke a procedure via a CALL statement
Example: CALL SUBRT(A,B,C);
Subroutines compiled separately are called EXTERNAL procedures and their name is
limited to seven characters. (Although MVS supports eight, the last position is used by
the PL/I compiler for a suffix to segregate code from one external procedure into more
than one CSECT).
A STOP or EXIT routine in a procedure stops or exits the whole run unit.
Example:
MAINPR: PROCEDURE OPTIONS(MAIN);
DCL X FIXED DEC(7,2);
DCL Y FIXED DEC(7,2);
DCL Z FIXED DEC(8,2);
DCL SUBRT ENTRY; /* declares SUBRT as a procedure*/
GET LIST (X,Y);
CALL SUBRT(X,Y,Z);
PUT LIST('THE SUM IS =',Z);
END MAINPR;
SUBRT: PROCEDURE(A,B,C);
DCL A FIXED DEC(7,2);
DCL B FIXED DEC(7,2);
DCL C FIXED DEC(8,2);
C = A + B;
END SUBRT;
An argument can be one of the following
Variable
Constant
Expression
Array name
Array expression
Major structure name
Minor structure name
Structure expression
Built in function name
Entry name
(can be an Entry Variable or a Label variable)
File name
Example:
CALL SUBRT('ABCD',VAR_1);
CALL SUBRT(VAR_2,SQRT(X)); /*SQRT is invoked before SUBRT is called */
DAY = SUBSTR(DATE,5,2);
/* since DATE is declared as builtin it is
invoked before SUBSTR is called
*/
When a constant is passed as an argument, internally a dummy argument is generated

PL/I

23/01/2006

Page:- 33 / 119

to handle this argument


Example: CALL SUBRT(5.7,X);
Note that the dummy argument will have attribute FIXED(2,1) whereas in SUBRT the
first argument was FIXED(7,2). This can cause error. To avoid this pitfall code as below
Example:
DCL ARG_1 FIXED DECIMAL(7,2);
ARG_1 = 5.7;
CALL SUBRT(ARG_1,X);
Alternately declare the subroutine procedure as below
Example:
DCL SUBRT ENTRY(FIXED DECIMAL(7,2),
FIXED DECIMAL(7,2),
FIXED DECIMAL(8,2));
A dummy argument is generated under following conditions
If an argument is a constant
If an argument is an expression involving operators
If an argument is an expression in parenthesis
If an argument is a variable whose attributes are different from those the declared for
the parameter in the ENTRY name attribute specification appearing in the invoking
block
If an argument is itself a function reference returning a value
Note that any modification made to a parameter which is represented by a dummy
argument is not reflected in the original parameter.
Function Procedures
Return statement is used to terminate a function
RETURN(element-expression);
Example : W = CALC(A,B);
MAINPR: PROCEDURE OPTIONS(MAIN);
DCL CALC ENTRY RETURNS(FIXED DEC(7));
DCL SUM FIXED DECIMAL(7);
DCL A FIXED DECIMAL(7);
DCL B FIXED DECIMAL(7);
DCL C FIXED DECIMAL(7);
GET LIST(A,B,C);
SUM = CALC(A,B,C);
PUT LIST('SUM IS',SUM);
END MAINPR;
CALC: PROCEDURE (X,Y,Z) RETURNS(FIXED DECIMAL(7));
DCL X FIXED DECIMAL(7);
DCL Y FIXED DECIMAL(7);
DCL Z FIXED DECIMAL(7);
RETURN(X+Y+Z);
END CALC;
Scope of Identifiers

PL/I

23/01/2006

Page:- 34 / 119

P1:PROCEDURE;
DCL X CHAR(2);
DCL Y CHAR(2);
DCL Z CHAR(2);
DCL SUM FIXED DEC(7);
.
.
CALL P2;
.
.
P2:PROCEDURE;
DCL A CHAR(2);
DCL B CHAR(2);
DCL C CHAR(2);
DCL SUM CHAR(7);
/* this hides the SUM in P1
*/
X = 'AB';
/* Parameters in outer block can be accessed */
.
.
END P2;
END P1;
Note: X,Y and Z are known to P1.However A,B and C are not known to P1
A,B and C come into existence only when P2 is invoked
X,Y,Z,A,B and C are all known to P2.
Inner blocks can see out. Outer blocks cannot see inwards.
Any data item with the EXTERNAL attribute tells the compiler that this variable is known
outside this module and is to be allocated once in the Global data area. However all other
modules which refer to this variable must declare this variable with same name and
attributes including the EXTERNAL attribute.
Classification of Built In Functions
To aid in their description, built-in functions are listed in classes below. The first four
classes are computational built-in functions.
String-handling
Arithmetic
Mathematical
Array-handling
Condition-handling
Storage control
Event
Input / Output
Miscellaneous
String-Handling Built In Functions
The string-handling built-in functions simplify the processing of bit and character strings.
They are:
BIT
BOOL
CHAR
GRAPHIC

HIGH
INDEX
LENGTH
LOW

Arithmetic Built In Functions

MPSTR
REPEAT
STRING
SUBSTR

TRANSLATE
UNSPEC
VERIFY

PL/I

23/01/2006

Page:- 35 / 119

The arithmetic built-in functions allow you to:


1. Control conversion of base, scale, mode, and precision both directly and during
basic arithmetic operations.
2. Determine properties of arithmetic values. For example, the SIGN function indicates
the sign of an arithmetic value.
They are:
ABS
ADD
BINARY
CEIL
COMPLEX
CONJG

DECIMAL
DIVIDE
FIXED
FLOAT
FLOOR

IMAG
MAX
MIN
MOD
MULTIPLY

PRECISION
REAL
ROUND
SIGN
TRUNC

Mathematical Built In Functions


The mathematical built-in functions provide mathematical operations. They are:
ACOS
COSD
LOG
SINH
ASIN
COSH
LOG2
SQRT
ATAN
ERF
LOG10
TAN
ATAND
ERFC
SIN
TAND
ATANH
EXP
SIND
TANH
All of these functions operate on floating-point values to produce a floating-point result.
Array-Handling Built In Functions
The array-handling built-in functions operate on array arguments and return an element
value . They are:
ALL
LBOUND
ANY
POLY
DIM
PROD
HBOUND
SUM
Condition-Handling Built In Functions
The condition-handling build-in functions allow you to investigate the cause of
enabled conditions. They are:
DATAFIELD
ONCHAR
ONCODE
ONCOUNT

ONFILE
ONKEY
ONLOC
ONSOURCE

Use of these functions is in context (returns a meaningful value) when within the scope of
an ON-unit entered for the condition specific to the built-in function, or within an ON-unit
for the ERROR or FINISH condition when raised as an implicit action. All other uses are
out of context.
Storage Control Built In Functions
The storage-control built in functions allow you to determine the storage requirements and
location of variables, to assign special values to area and locator variables, to perform
conversion between offset and pointer values, and to obtain the number of generations of
a controlled variable. They are:
ADDR
ALLOCATION

ENTRYADDR
NULL

POINTERADD
POINTERVALUE

PL/I

23/01/2006
BINARYVALUE
CURRENTSTORAGE
EMPTY

OFFSET
POINTER

Page:- 36 / 119

STORAGE
SYSNULL

Input / Output Built In Functions


The Input / Output built-in functions allow you to determine the current state of a file.
They are:
COUNT
LINENO
SAMEKEY
Miscellaneous Built In Functions
The built-in functions that do not fit into any of the foregoing classes are:
DATE
DATETIME
PLIRETV
TIME
PLIRETC
Built IN Subroutines
The PL/I built-in subroutines are the following;
PLICANC
PLISRTC
PLICKPT
PLISRTA
PLIDUMP
PLISRTB

PLISRTD
PLITEST

Pseudo-variables
Pseudo-variables represent receiving fields. Except when noted in the description, the
Pseudo-variables :
Can appear on the left of the assignment symbol in an assignment.
Can appear in a data list of a GET statement or in the STRING option of a PUT
statement.
Some of the commonly used Pseudo-variables are:
ONCHAR
ONSOURCE
STRING
SUBSTR
ADDR (Storage control)
ADDR returns the pointer value that identifies the generation of x. The syntax for ADDR
is:
ADDR (X) Reference to variable of any data type,
ALLOCATION (Storage Control)
ALLOCATION returns a FIXED BINARY (31,0) value specifying the number of
generations that can be accessed in the current task for X. The syntax for ALLOCATION
is:
ALLOCATION (X)
BINARYVALUE (Storage Control)
BINARYVALUE returns a REAL FIXED BIN (31,0) value that is the converted value of its
pointer expression, X. The syntax for BINARYVALUE is:

PL/I

23/01/2006

Page:- 37 / 119

BINARYVALUE (X)
COUNT (Input / Output)
COUNT returns a FIXED BINARY (15,0) value specifying the number of data items
transmitted during the last GET or PUT operation on X. The syntax for COUNT is:
COUNT (X)
X is a File-reference. The file must be open and have the STREAM attribute.
CURRENTSTORAGE (Storage Control)
CURRENTSTORAGE returns a FIXED BINARY (31,0) value giving the implementationdefined storage in bytes, required by X . The syntax for CURRENTSTORAGE is:
CURRENTSTORAGE (X)
DATAFIELD (Condition-Handling)
DATAFIELD is in context in a NAME condition ON-unit (or any of its dynamic
descendants), and returns a character string whose value is the contents of the field that
raised the condition.
DATAFIELD ( )
DATE (Miscellaneous)
DATE returns a character string, length 6, in the format YYMMDD. The syntax for DATE
is:
DATE ( )
The returned character string represents:
YY
last two digits of the current year
MM
Current month
DD
Current day
DATETIME (Miscellaneous)
DATATIME returns a character string, length 17, in the format of
YYYYMMDDHHMMSSTTT. The syntax for DATATIME is:
DATETIME ( )
The returned character string represents:
YYYY
MM
DD
HH
MM
SS
TTT

Current year
Current month
Current day
Current hour
Current minute
Current second
Current millisecond

DIM (Array-Handling)
DIM returns a FIXED BINARY (31,0) value specifying the current extent of the dimension
Y of X . The syntax for DIM is:

PL/I

23/01/2006

Page:- 38 / 119

DIM (X, Y)
X
Array expression. X must not have less than Y dimensions, and X must
not be an array of structures.
Y
Expression specifying a particular dimension of X. If necessary, Y is
converted to a FIXED BINARY (31,0) value. Y must be greater than or equal to 1
If the extent of an array dimension exceeds the allowable number for the implementation,
the DIM function returns an undefined value.
EMPTY (Storage Control)
EMPTY returns an area of zero extent. It can be used to free all allocations in an area.
The syntax for EMPTY is:
EMPTY ( )
Example
DECLARE A AREA,
I BASED (P);
J BASED (Q);
ALLOCATE I IN (A), J IN (A);
A = EMPTY( );
/*equivalent to: FREE I IN (A), J IN (A);*/
HBOUND (Array-Handling)
HBOUND returns a FIXED BINARY (31,0) value specifying the current upper bound of
dimension Y of X. The syntax for HBOUND is:
HBOUND (X, Y)
X Array expression. X must not have less than Y dimensions, and X must not be an
array of structures.
Y Expression specifying a particular dimension of X. If necessary Y is converted to a
FIXED BINARY (15,0) value. Y must be greater than or equal to 1
INDEX (String-Handling)
INDEX returns a FIXED BINARY (15,0) value indicating the starting position within X of a
sub-string identical to Y. The syntax for INDEX is:
INDEX (X, Y)
X
String-expression to be searched.
Y
String-expression to be searched for.
if Y does not occur in X, or if either X or Y have zero length, the value zero is returned
If occurs more than once in X, the starting position of the leftmost occurrence is returned.
LBOUND (Array-Handling )
LBOUND returns a FIXED BINARY (31,0) value specifying the current lower bound of
dimension Y of X. The syntax for LBOUND is:
LBOUND (X, Y)

PL/I

23/01/2006

Page:- 39 / 119

X
Array expression, X must not have less than Y dimensions, and X must
not be an array of structures.
Y
Expression specifying the particular dimension of X. If necessary Y is
converted to a FIXED BINARY (15,0) value . Y must be greater than or equal to 1
LENGTH (String-Handling)
LENGTH returns a FIXED BINARY (15,0) value specifying the current length of X. The
syntax for LENGTH is:
LENGTH (X)
X
String -expression. If X is binary it is converted to bit string; otherwise
any other conversion required is to character string.
LINENO ( Input / Output)
LINENO returns a FIXED BINARY (15,0) value specifying the current line number of X.
The syntax for LINENO is:
LINENO (X)
X
File-reference.
The file must be open and have the PRINT attribute.
NULL (Storage Control)
NULL returns the null pointer value . The null pointer value does not identify any
generation of a variable The null pointer value can be converted to OFFSET by
assignment of the built-in function value to an offset variable. The syntax for NULL is:
NULL ( )
OFFSET (Storage Control)
OFFSET returns an offset value derived from a pointer reference X and relative to an
area Y. If X is the null pointer value, the null offset value is returned. The syntax for
OFFSET is:
OFFSET (X, Y)
X
Pointer reference, which must identify a generation of a based variable
within the area Y, or be the null pointer value.
Y
Area reference.
ONCHAR (Condition-Handling)
ONCHAR returns a character string of length 1, containing the character that caused
the CONVERSION condition to be raised. It is in context in an ON-unit (or any of its
dynamic descendants) for the CONVERSION condition or for the ERROR or FINISH
condition raised as the implicit action for the CONVERSION condition. The syntax for
ONCHAR is:
ONCHAR ( )
ONCHAR ( Pseudo-variable )
The Pseudo-variable sets the current value of the ONCHAR built-in function. The
element value assigned to the pseudo-variable is converted to a character value of length
1. The new character is used when the conversion is re-attempted.
ONCHAR ( )

PL/I

23/01/2006

Page:- 40 / 119

ONCODE (Condition-Handling)
ONCODE returns FIXED BINARY (15,0) value that is the condition code. It is in context in
any ON-unit, or any dynamic descendant of an ON-unit.
ONCODE ( )
ONFILE (Condition-Handling)
ONFILE returns a character string whose value is the name of the file for which an
input/output or CONVERSION condition is raised.
ONFILE ( )
ONLOC (Condition-Handling)
ONLOC returns a character string whose value is the name of the entry-point used for the
current invocation of the procedure in which a condition was raised. It is in context in
any ON-unit, or in any of its dynamic descendants.
ONLOC ( )
ONSOURCE (Condition - Handling)
ONSOURCE returns a character string whose value is the contents of the field that was
being processed when the CONVERSION condition was raised. It is in context in an ONunit, or any of its dynamic descendants, for the CONVERSION condition or for the
ERROR or FINISH condition raised as the implicit action for the CONVERSION
CONDITION. The syntax for ONSOURCE is:
ONSOURCE ( )
ONSOURCE (Pseudo-variable)
The pseudo-variable sets the current value of the ONSOURCE built-in function. The
element value assigned to the pseudo-variable is converted to a character string and, if
necessary, is padded on the right with blanks or truncated to match the length of the field
that raised the CONVERSION condition. The new string is used when the conversion is
re-attempted.
ONSOURCE ( )
PLIRETC (Built-in Subroutine)
This built-in subroutine allows you to set a return code that can be examined by the
program or (sub) system that invoked this PL/I program or by another PL/I procedure via
the PLIRETV built-in function. The syntax for PLIRETC is:
PLIRETC (return-code)
Example: CALL PLIRETC(16); /* sets return code of 16 */
PLIRETV (Miscellaneous)
PLIRETV returns a FIXED BINARY (31,0) value that is the PL/I return code. The syntax
for PLIRETV is:

PL/I

23/01/2006

Page:- 41 / 119

PLIRETV ( )
The value of the PL/I return code is the most recent value specified by a CALL PLIRETC
statement in any task or the value returned by a COBOL or assembler routine (via
Register 15) whose entry point is declared with the option OPTIONS(RETCODE), or
zero.
POINTER (Storage Control)
POINTER returns a pointer value that identifies the generation specified by an offset
reference X, in an area specified by Y. If X is the null offset value, the null pointer value is
returned. The syntax for POINTER is:
POINTER (X, Y)
X
Y

Offset reference, which can be the null offset value; if it is not, it must
identify a generation of a based variable.
Area reference.

STORAGE (Storage Control)


STORAGE returns a FIXED BINARY (31,0) value giving the implementation - defined
storage, in bytes, allocated to a variable X. The syntax for STORAGE is:
X

STORAGE ( X )
A variable of any data type, data organisation, alignment

STRING (String-Handling)
STRING returns an element bit or character string that is the concatenation of all the
elements of X. The syntax for STRING is:
STRING ( X )
X

Aggregate or element reference. Each base element of X must be either


all bit-string, or all character string and/or numeric character, in any
combination. If X is a structure that has padding caused by ALIGNED
elements, the padding is not included in the result. If any of the strings in
the aggregate X are of varying length, only the current length, not
including the 2-byte length prefix, is concatenated. if X is an element
variable, the rules for aggregates apply except that there is no
concatenation.

SUBSTR (String-Handling)
SUBSTR returns a sub-string, specified by Y and Z, of X. The syntax for SUBSTR is:
SUBSTR ( X, Y, Z)
X

String-expression from which the sub-string is to be extracted. If X is not


a string, it is converted to a bit string if binary, or a character string if
decimal.

Expression that can be converted to a FIXED BINARY (15,0) value


specifying the starting position of the sub-string in X.

PL/I

23/01/2006
Z

Page:- 42 / 119

Expression that can be converted to a value specifying the length of the


sub-string in X. If Z is zero, a null string is returned. if Z is omitted, the
sub-string returned is position Y in X to the end of x.

The STRINGRANGE condition is raised if Z is negative or if the values of Y and Z are


such that the sub-string does not lie entirely within the current length of X; it is not raised
when Y = LENGTH(X)+1 and Z=0 or Z is omitted.
SUBSTR (Pseudo-variable)
The pseudo-variable assigns a string value to a sub-string, specified by Y and Z, of X.
The remainder of X is unchanged. (Assignments to a varying string do not change the
length of the string.) The syntax for SUBSTR pseudo-variable is:
SUBSTR (X,Y,Z) = ..
X

String-reference. X must not be a numeric character.

Expression that can be converted to a FIXED BINARY (15,0) value


specifying the starting position of the sub-string in X.

Expression that can be converted to a FIXED BINARY (15,0) value


specifying the length of the sub-string in X. If Z is zero, a null string is
returned. If Z is omitted, the sub-string returned is position Y in X to the
end of X. Y and Z can be arrays only if X is an array.

SYSNULL (Storage Control)


SYSNULL returns the system null pointer value. It can be used to initialise static pointer
and offset variables. It also can be assigned or converted to offset variables (like NULL).
The Syntax for SYSNULL is :
SYSNULL ( )
TIME(Miscellaneous)
TIME returns a character string, length 9, in the format of HHMMSSTTT. The syntax for
TIME is:
TIME ( )
The returned character string represents:
HH
Current hour
MM
Current minute
SS
Current second
TTT
Current millisecond
TRASLATE (String-Handling)
TRANSLATE returns a character string of the same length as X. The Syntax for
TRANSLATE is:
TRANSLATE (X, Y, Z)
X

Character expression to be searched


characters.

for possible translation of its

Character expression containing the translation values of characters.

PL/I

23/01/2006

Page:- 43 / 119

Character expression containing the characters that are to be translated.


If Z is omitted, a string of 256 characters is assumed; it contains one
instance of each EBCDIC code arranged in ascending collating
sequence (hexadecimal 00 through FF)

TRANSLATE operates on each character of X as follows. If a character in X is


found in Z, the character in Y that corresponds to that in Z is copied to the result;
otherwise, the character in X is copied directly to the result. if Z contains
duplicates, the leftmost occurrence is used. Y is padded with blanks, or
truncated, on the right to match the length of Z.
Any arithmetic or bit arguments are converted to character.
Example:
DECLARE (W, X) CHAR (3);
X = ABC;
W = TRANSLATE (X, TAR, DAB);
/* W = ARC */
VERIFY (String- Handling)
VERIFY returns a FIXED BINARY (15,0) value indicating the leftmost character or bit
position in X that is not in Y. If all the characters or bits in X do appear in Y, a value of
zero is returned. If X is the null string, a value of zero is returned. If X is not the null string
and Y is the null string, a value of one is returned. The syntax for VERIFY is:
VERIFY (X, Y,)
X
String Expression
Y
String expression .
If either argument is character or decimal, conversions are performed to produce
character string. Otherwise, if the arguments are bit and binary or both binary, and
conversions are performed to produce bit strings. In the following example, the VERIFY
built-in function is used to test whether or not a string is all-numeric:
VERIFY (X, 0123456789).

PL/I

23/01/2006

SECTION 6
Logical Testing
Symbols
GE or >=
GT or >
NE or
=
LT or <
LE or <=
NL or
NG or

Page:- 44 / 119

CONTROL STATEMENTS
Operations
Greater than or equal to
Greater than
Not equal to
Equal to
Less than
Less than or equal to
Not less than
Not greater than

IF logical expression THEN statement ;


ELSE statement;
IF logical expression THEN
DO;
.
.
END;
ELSE
DO;
.
.
END;
Nested IF statement
IF A=B THEN
IF A=C THEN
X=1;
ELSE
X=2;
ELSE
X=3;
Null IF
IF A=B THEN
IF A=C THEN
X=1;
ELSE;
ELSE
X=3;
Select Statement
SELECT (expression which is optional);
WHEN (expression) action 1;
WHEN (expression) action 2;
WHEN (expression) action 3;
OTHERWISE action 3;
END;
Example:
SELECT (CODE)

IF A=B THEN
IF A=C THEN
X=1;
ELSE
X=3;

back

PL/I

23/01/2006

Page:- 45 / 119

WHEN (1) DO;


.
.
END;
WHEN (2) CALL SUBRTN;
OTHERWISE CALL INVCODE;
END;
SELECT;
WHEN(CODE<10) DO;
.
.
END;
WHEN (CODE>=10) CALL SUBRTN;
OTHERWISE CALL ERRORTN;
END;
Logical Operators
NOT
&
|

AND
OR

Note that the | or


can be changed by the %PROCESS statement like below
%PROCESS NOT('~'); /* ~ becomes the not operator */
IF A=B & C=D THEN CALL SUBRTN;
Logical Operators in the assignment statement
A=B=C; /*If B=C then a value of 1 is assigned to A else 0 is assigned */
A=B>C; /* If B>C then a value of 1 is assigned to A else 0 is assigned*/
DO LOOPS
DO UNTIL(expression) /* test is after the loop body
*/
/* loop terminates when the condition becomes true */
DO WHILE(expression) /* test is before the loop body
*/
/* loop terminates when the condition becomes false*/
DO UNTIL (expression);
.
.
END;
Expression can be a logical expression or a flag variable (BIN (1)). A flag variable
with a value of binary one is true. A binary value of zero is false.
Iterative DO loop
DO control-variable = initial value TO limit value BY modification value
[WHILE(expression) | UNTIL(expression)];
Example:
J=10;
K=5;
L=2;
DO I=J TO K BY L;

PL/I

23/01/2006

Page:- 46 / 119

.
.
END;
Example(s):
DO I=1 TO 100 BY 1;

/* DO I=1 BY 1 TO 100 is also OK

*/

DO I=100 TO 1 BY -1;
DO I=1 BY 1;

/*termination condition must be inside the loop*/

DO I=0.1 BY 0.1 TO 1.0;


DO I=1 TO 5,10 TO 20, 25 TO 50 BY 2;
DO I=1,4,7,22;
DO I=1 TO 100 WHILE(X<100);
/* loop terminates when I reaches 100 or >=100*/
Commas separate specifications as below
DO I=1 TO 10, 11 BY 0 WHILE(A=B);
/* after I = 1 to 10, continuos looping at I = 11 takes place while
A=B. At this time loop terminates only when A is not = B */
DO I='ABC','DEF','GHI'
Leave statement
The leave statement transfers control from within a DO loop to the statement
following the end statement that delimits the group and terminates the DO group.
Example
A:
DO;
.
.
LEAVE; /* transfers control to the next statement after this block*/
.
END;
next statement;
Example
A: DO I =1 to 10;
DO J = 1 to 10;
IF X(I,J)=0 THEN LEAVE A; /* to statement outside group A
ELSE..
.
END;
END;
statement after group A.

*/

PL/I

23/01/2006

SECTION 7

Page:- 47 / 119

CONDITIONS AND ON UNITS

back

Condition and ON-UNITS


condition occurs

WHAT
system
ACTION ? action

action (null operation)

no

Program specified action


In the absence of a program specified action, the default system action takes place. The
default system action is usually the ERROR condition is raised and the program is
terminated. Two built-in functions exist, ONCODE (which returns the error code, binary
value, for diagnostic purposes) and ONLOC which returns the procedure/function name,
as a string, where the error occurred. Note that these built-in functions do not have any
arguments and should therefore be declared before use as below
DCL (ONCODE,ONLOC) BUILTIN;
Standard error handling routines can be kept in source form in a source statement library
and included in your program by the statement
%INCLUDE

book_name

ON units can be declared to handle specific conditions. Within an ON unit facilities are
provided to get more information on the condition that caused the ON unit to be activated.
Some of these are ONCODE( ), ONSOURCE( ), ONCHAR( ). A normal return from an
ON unit is said to take place when execution returns through END statement. An
abnormal termination is said to occur when we branch out of the ON unit through a
GOTO statement. You cannot code a RETURN statement in an ON unit.
ON condition
BEGIN;
.
.
END;
Example:
ON ENDFILE(SYSIN)
BEGIN
.
.
END;
Example:
ON ERROR SYSTEM; /* restores back system action for ERROR condition*/
ON ERROR
BEGIN;
ON ERROR SYSTEM;
PUT DATA;
/* output all variables and values
*/

PL/I

23/01/2006

Page:- 48 / 119

END;
Condition Handling
Condition Prefixes
You can specify whether or not some conditions are enabled or disabled. If a condition is
enabled, the raising of the condition executes an action. If a condition is disabled, the
raising of the conditions does not execute an action.
Enabling and disabling can be specified for the eligible conditions by a condition prefix.
Example:
(SIZE): L1 X=(I**N); /* enable SIZE condition for this statement only */
The conditions that are always enabled unless they are explicitly disabled by condition
prefixes are:
CONVERSION
FIXEDOVERFLOW
OVERFLOW
UNDERFLOW
ZERODIVIDE
Each of the preceding conditions can be disabled by condition prefix specifying the
condition name preceded by NO with intervening blanks as below.
NOCOVERSION
NOFIXEDOVERFLOW
NOOVERFLOW
NOUNDERFLOW
NOZERODIVIDE
The conditions that are always disabled unless they are enabled by a condition prefix are:
SIZE
SUBSCRIPTRANGE
STRINGRANGE
STRINGSIZE
All other conditions are always enabled and cannot be disabled . These conditions are:
AREA
ATTENTION
CONDITION
ENDFILE
ENDPAGE
ERROR
FINISH

KEY
NAME
RECORD
TRANSMIT
UNDEFINEDFILE
PENDING

Scope of the Condition Prefix


A condition prefix attached to a PROCEDURE or BEGIN statement applies to all the
statements up to and including the corresponding END statement. This includes other
PROCEDURE or BEGIN statements nested with that block.
The scope of a condition prefix applied to a DO or SELECT statement is limited to
execution of the statement itself; it does not apply to execution of the entire group.
ON Statement

PL/I

23/01/2006

Page:- 49 / 119

The ON statement establishes the action to be executed for any subsequent raising of
an enabled condition in the scope of the established action.
ON condition [SNAP] [SYSTEM] | on-unit
SNAP
Specifies that when the enabled condition is raised, a list is printed of all the blocks and
ON-units active in the current task at the time the condition is raised. The action of the
SNAP option precedes the action of the ON-unit.
SYSTEM
Specifies that the implicit action is taken. The implicit action is not the same for every
condition, although for most conditions a message is printed and the ERROR condition is
raised.
on-unit
Specifies the action to executed when the condition is raised and is enabled. The action
is defined by the statement or statements in the ON-unit itself. The On-unit is not
executed at the time the ON statement is executed; it is executed only when the specified
enabled condition is raised.
null on-Unit
The effect of a null statement on-unit is to execute normal return from the condition. Use
of the null on-unit is not the same as disabling, for two reasons:
A null ON-unit can be specified for any condition, but not all conditions can be
disabled.
Disabling a condition, if possible, can save time by avoiding any checking for this
condition. (If a null ON-unit is specified, the system must still check for the raising of
the condition).
The execution of an ON statement establishes an action specification for a condition.
Once this action is established, it remains established throughout that block and
throughout all dynamically descendent blocks (blocks entered via CALL or a function
reference) until it is overridden by the execution of another ON statement or a REVERT
statement or until termination of the block in which the ON statement is executed.
Example for REVERT:PROC1: PROCEDURE;
ON CONVERSION BEGIN;
END;
B: PROC2;
ON CONVERSION BEGIN;
END;
.
.
REVERT CONVERSION;
END PROC2;
END PROC1;

/* Overrides ON unit in PROC1 */

/* Now the ON unit in PROC1 is back in control */

PL/I

23/01/2006

Page:- 50 / 119

Note: Dynamic descendency refers to the fact that ON-units are inherited from the
calling procedure in all circumstances. Dynamic descendency is not known until run time,
since a procedure can be called from anywhere where it is visible.
SIGNAL Statement
You can raise a condition by means of the SIGNAL statement. This statement can be
used in program testing to verify the action of an ON-unit and to determine whether the
correct action is associated with the condition. The established action is taken unless the
condition is disabled.
If the specified condition is disabled, the SIGNAL statement becomes equivalent to a null
statement. The syntax for the SIGNAL statement is:
SIGNAL Condition;
Classification of Conditions
The conditions are classified as follows:
Computation conditions-those associated with data handling, expression evaluation,
and computation. The conditions are:
CONVERSION
SIZE
FIXEDOVERFLOW
UNDERFLOW
OVERFLOW
ZERODIVIDE
If a computational conditional (except UNDERFLOW) is raised and the condition is
disabled, the program is in error;
Input/output conditions-those conditions associated with input and output.
They are:
ENDFILE
KEY
UNDEFINEDFILE
ENDPAGE
TRANSMIT
NAME
RECORD

When end of file is reached


Incorrect KEY for a keyed data set
File could not be opened
End of page on PRINT file
I/O error
On GET DATA when incorrect items in input
Typically when buffer is too small for record
For read. For write of fixed length records it
Can occur if buffer is larger than record size.

Program-checkout conditions-those conditions that facilitate the debugging of a


program. They are
STRINGSIZE
STRINGRANGE
SUBSCRIPTRANGE
If SUBSCRIPTRANGE is raised is disabled, the program is in error. Because this
checking involves a substantial overhead in both storage space and run time, it
usually is used only in programs testing. It is removed for production programs,
because the above are normally disabled conditions.
Miscellaneous conditions, which are:
AREA , ERROR, FINISH, CONDITION
AREA Condition

PL/I

23/01/2006

Page:- 51 / 119

The AREA condition is raised in either of the following circumstances:


When an attempt is made to allocate a based variable within an area that contains
insufficient free storage for the allocation to be made.
When an attempt is made to perform an area assignment, and the target area
contains insufficient storage to accommodate the allocations in the source area.
The syntax for AREA is:
ON AREA
BEGIN;
.
END;
Result: In both cases the attempted allocation or assignment has no effect.
Implicit Action: A message is printed and the ERROR condition is raised.
Status: AREA is always enabled.
Normal Return
1) If the condition was raised by an allocation and the on-unit is a NULL on-unit the
allocation is not attempted again.
2) If the on-unit is not NULL then the allocation is attempted again provided the
pointer qualifying the reference to the AREA has been changed
3) If the condition was raised by an area assignment or by a SIGNAL statement, the
execution continues from the next statement.
CONDITION Condition
The CONDITION condition is raised by a SIGNAL statement that specifies the
appropriate name. The name specified in the SIGNAL statement determines which
CONDITION condition is to be raised. The syntax for CONDITION is:
ON CONDITION (name)
Abbreviation: COND
The CONDITION condition allows you to establish an ON-unit that is executed whenever
a SIGNAL statement is executed specifying CONDITION and that name.
As a debugging aid, this condition can be used to establish an ON-unit whose execution
results in printing information that shows the current status of the program. The On-unit
can be executed from any point in the program through placement of SIGNAL statement.
Of course, normal rules of name scope apply;
Following is an example of how the CONDITION condition might be included in a
program:
DCL TEST CONDITION;
/* declare the TEST condition */
ON CONDITION (TEST) BEGIN;
/*Now set up the ON unit
*/
.
.
.
END;
The begin-block is executed whenever the following statement is executed:
SIGNAL CONDITION (TEST);

PL/I

23/01/2006

Page:- 52 / 119

Implicit Action: A message is printed and execution continues with the statement
following SIGNAL.
Status: CONDITION is always enabled.
Normal Return: Execution continues with the statement following the SIGNAL statement.
CONVERSION Condition
The CONVERSION computational condition is raised whenever an invalid conversion is
attempted on character data.
A character other than 0 or 1 exists in character data being converted to bit data.
A character value being converted to a numeric character field, or to coded arithmetic,
contains characters which are not the representation of an optionally signed arithmetic
constant, or an expression to represent a complex constant .
A value being converted to a character pictured item contains characters not allowed
by the picture specification.
The syntax for CONVERSION is;
ON CONVERSION .
Implicit Action: A message is printed and the ERROR condition is raised.
Status: CONVERSION is enabled throughout the program, except within the scope of a
condition prefix specifying NOCONVERSION.
Normal Return: If the ONSOURCE or ONCHAR PSEUDOVARIABLE is used, the
program retries the conversion on return from the on-unit. If the error is not corrected, the
program loops. If these PSEUDOVARIABLES are not used the ERROR condition is
raised.
Example
DCL X BIT(4);
X = '10A1';
/* all chars must be 1 or 0

*/

ENDFILE Condition
The ENDFILE input \ output condition can be raised during a GET or READ operation by
an attempt to read past the end of the file specified in the GET or READ statement. It
applies only to SEQUENTIAL INPUT, SEQUENTIAL UPDATE, and STREAM INPUT files.
The syntax for ENDFILE is:
ON ENDFILE (file-reference) ..
In record-oriented data transmission, ENDFILE is raised whenever an end of file is
encountered during the execution of a READ statement.
In stream-oriented data transmission, ENDFILE is raised during the execution of a GET
statement if an end of file is encountered either before any items in the GET statement
data list have been transmitted or between transmission of two of the data items. If an

PL/I

23/01/2006

Page:- 53 / 119

end of file is encountered while a data item is being processed, or if it is encountered


while an X format item is being processed, the ERROR condition is raised.
If the file is not closed after ENDFILE is raised, any subsequent GET or READ statement
for that file immediately raises the ENDFILE condition again.
Implicit Action: A message is printed and the ERROR condition is raised.
Status: The ENDFILE condition is always enabled.
Normal Return: Execution continues with the statement immediately following the GET or
READ statement that raised this condition. If a file is closed in an on-unit for this
exception, the results of normal return is undefined. Exit in such situations must be by a
GO TO from the on-unit block.
ENDPAGE Condition
The ENDPAGE input/output condition is raised when a PUT statement results in an
attempt to start a new line beyond the limit specified for the current page. This limit can
be specified by the PAGESIZE option in an OPEN statement; if PAGESIZE has not been
specified, a default limit of 60 is applied. The attempt to exceed the limit can be made
during data transmission (including associated format items, if the PUT statement is editdirected), by the LINE option, or by SKIP option. ENDPAGE can also be raised by a LINE
option or LINE format item that specified a line number less than the current line number .
The syntax for ENDPAGE is
ON ENDPAGE (file-reference)
ENDPAGE is raised only once per page, except when it is raised by the SIGNAL
statement. We can artificially signal this condition with SIGNAL ENDPAGE(filename);
The ON-unit can start a new page by execution of a PAGE option or a PAGE format
item, which sets the current line to one.
If the ON-unit does not start a new page, the current line number can increase
indefinitely
Implicit Action: A new page is started. If the condition is signalled, execution is unaffected
and continues with the statement following the SIGNAL statement.
Status: ENDPAGE is always enabled.
Normal Return: Execution of the PUT statement continues and the data is written on the
current line which may(probably would) have been changed by the ON UNIT.
ERROR Condition
The ERROR condition is raised under the following circumstances:
Provides common condition that can be used to check many other conditions
As a result of the implicit action for a condition for which that action is to print an error
message and raise the ERROR condition.
As a result of an error (for which there is no other condition) during program
execution.
As a result of an ABEND
As a result of a SIGNAL ERROR statement.
Use ONCODE built-in to distinguish between various circumstances that can raise
ERROR condition
The syntax for ERROR is:

PL/I

23/01/2006

Page:- 54 / 119

ON ERROR
Implicit Action: If the condition is raised in the major task, the FINISH condition is raised
and the task terminates. If the condition is raised in any other task, the program is
terminated.
Status: ERROR is always enabled
Normal Return : The implicit action is taken.
FINISH Condition
The FINISH condition is raised during execution of a statement that would terminate the
major task of the PL/I program, that is, by a STOP statement in any task, or an EXIT
statement in the major task, or a RETURN or END statement in the MAIN procedure of
the program.
Note: The STOP statement immediately terminates the program including all concurrent
tasks. Before termination the FINISH condition is raised in the task in which the STOP
executes. On normal return from the on-unit all tasks in the program terminate.
The EXIT statement immediately terminates the program or the task that contains the
statement and all tasks attached by this task. If executed in a major task, EXIT raises the
FINISH condition in that task. On normal return from the on-unit, the task executing the
statement and all of its descendant tasks are terminated. Thus EXIT in a major task is
equivalent to a STOP statement.
The condition is also raised by SIGNAL FINISH, and as part of the implicit action for the
ERROR condition. The condition is raised in the task in which the statement is executed,
and any ON-unit specified for the condition is executed as part of that task. An abnormal
return from the ON-unit avoids program termination and allows the program to continue.
The syntax for FINISH is:
ON FINISH
Implicit Action: No action is taken and processing continues from the point where the
condition was raised.
Status: FINISH is always enabled.
Normal Return: Execution of the statement is resumed.
FIXEDOVERFLOW Condition
The FIXEDOVERFLOW computational condition is raised when the length of the result of
a fixed-point arithmetic operation exceeds the maximum length allowed by the
implementation.
The FIXEDOVERFLOW condition differs from the SIZE condition in that SIZE is raised
when a result exceeds the declared size of a variable, while FIXEDOVERFLOW is raised
when a result exceeds the maximum allowed by the computer. The syntax for
FIXEDOVERFLOW IS
ON FIXEDOVERFLOW ..

PL/I

23/01/2006

Page:- 55 / 119

Result: The result of the invalid fixed-point operation is undefined.


Implicit Action: A message is printed and the ERROR condition is raised.
Status: FIXEDOVERFLOW is enabled throughout the program, except within the scope
of a condition prefix that specifies NOFIXEDOVERFLOW.
Example
DCL
A
DCL
B
DCL
C
A=40000000;
B=80000000;
C=A*B;

FIXED DEC(15);
FIXED DEC(15);
FIXED DEC(15);
/* fixed point overflow is raised

*/

OVERFLOW
Occurs when the magnitude of the result of a floating point operation exceeds 10**75
A=55E71;
B=23E11;
C=A*B;
/* OVERFLOW condition is raised
*/
Result: The value of such an invalid floating point number is undefined
Implicit Action: A message is printed and ERROR is raised.
Status: Enabled throughout the program except within the scope of NOOVERFLOW
Normal Return: Control returns to the point after the instruction which caused this
condition.
SIZE Condition
The SIZE computational condition is raised only when high-order (that is, leftmost)
significant binary or decimal digits are lost in an attempted assignment to a variable or an
intermediate result or in an input/output operation. This loss can result from a conversion
involving different data types, different bases, different scales, or different precision. The
size condition is not enabled unless it appears in a condition prefix. The syntax for SIZE
is:
ON SIZE ..
The SIZE condition differs from the FIXEDOVERFLOW condition in that, whereas
FIXEDOVERFLOW is raised when the size of a calculated fixed-point value exceeds the
maximum allowed by the implementation, SIZE is raised when the size of the value being
assigned to a data item exceeds the declared (or default) size of the data item in your
program. SIZE can be raised on assignment of a value regardless of whether or not
FIXEDOVERFLOW was raised in the calculation of the value.
The declared size is not necessarily the actual precision with which the item is held
storage, however, the limit for SIZE is the declared or default size, not the actual size
storage. For example, a fixed binary item of precision (20) occupies a FULLWORD
storage, but SIZE is raised if a value whose size exceeds FIXED BINARY (20)
assigned to it.

in
in
in
is

PL/I

23/01/2006

Page:- 56 / 119

Because this checking involves a substantial overhead in both storage space and run
time, it usually is used only in program testing. You should remove it for production
programs.
If the SIZE condition is raised and it is disabled, the program is in error.
Result: The result of the assignment is undefined.
Implicit Action: A message is printed and the ERROR condition is raised.
Status: SIZE is disabled within the scope of a NOSIZE condition prefix and elsewhere
throughout the program, except within the scope of a condition prefix specifying SIZE.
Example
DCL
X
DCL
Y
X = Y;

FIXED DEC(4);
FIXED DEC(5) INIT(12345);
/* size condition is raised

*/

STRINGRANGE Condition
The STRINGRANGE program-checkout condition is raised whenever the values of the
arguments to a SUBSTR reference fail to comply with the rules described for the
SUBSTR built-in function. It is raised for each such reference. The syntax for
STRINGRANGE is:
ON STRINGRANGE.
Implicit Action: A message is printed and processing continues as described for normal
return.
Status: STRINGRANGE is disabled by default and within the scope of a
NOSTRINGRANGE condition prefix. It is enabled only within the scope of a
STRINGRANGE condition prefix.
Normal Return: Depends on various factors. (See PL/I language reference)
Example
DCL
NAME CHAR(20);
DCL
FIRST CHAR(16);
FIRST=SUBSTR(NAME,5,20); /* raises the string range condition

*/

/* STRINGRANGE is disabled by default. To enable it code as below */


(STRINGRANGE):
MAINLINE PROCEDURE OPTIONS(MAIN);
.
.
.
END MAINLINE;
STRINGSIZE Condition
The STRINGSIZE program-checkout condition is raised when you attempt to assign a
string to a target with a shorter maximum length. The syntax for STRINGSIZE is:
ON STRINGSIZE .

PL/I

23/01/2006

Page:- 57 / 119

Result: After the condition action, the truncated string is assigned to its target string. The
right-hand characters, bits or graphics of the source string are truncated so that the target
string can accommodate the source string.
Implicit Action; A message is printed and processing continues.
Status: STRINGSIZE is disabled by default and within the scope of a NOSTRINGSIZE
condition prefix. It is enabled only within the range of a STRINGSIZE condition prefix.
Example
DCL
NAME CHAR(20);
DCL
FIRST CHAR(16);
FIRST=NAME;
/* raises the stringsize condition
*/
/* To enable stringsize (which is off by default) for an assignment statement code */
/* below
*/
(STRINGSIZE):
RECEIVE = SUBSTR(FIELD_1,5,20);
SUBCRIPTRANGE Condition
The SUBSCRIPTRANGE program-checkout condition is raised whenever a subscript is
evaluated and found to lie outside its specified bounds.
ON SUBCRIPTRANGE .
Result: When SUBCRIPTRANGE has been raised, the value of the invalid subscript is
undefined, and, hence, the reference is also undefined.
Implicit Action: A message is printed and the ERROR condition is raised.
Status: SUBSCRIPTRANGE is disabled by default and within the scope of a
NOSUBSCRIPTRANGE condition prefix. It is enabled only within the scope of a
SUBCRIPTRANGE condition prefix.
Normal Return: Normal return from a SUBSCRIPTRANGE ON-unit raises the ERROR
condition .
UNDEFINEDFILE Condition
The UNDEFINEDFILE input/output condition is raised whenever a nonzero return code is
received from the OPEN SVC. If the attempt is made by means of an OPEN statement
that specifies more than one file, the condition is raised after attempts to open all files
specified. The syntax for UNDEFINEDFILE is:
ON UNDEFINEDFILE (file-reference) ..
Implicit Action: A message is printed and the ERROR condition is raised.
Status: UNDEFINDFILE is always enabled.
Normal Return: Upon the normal completion of the final ON-unit, control is given to the
statement immediately following the statement that raised the condition.
ZERODIVIDE Condition

PL/I

23/01/2006

Page:- 58 / 119

The ZERODIVIDE computational condition is raised when an attempt is made to divide


by zero. This condition is raised for fixed-point and floating-point division. The compiler
can also raise this condition, instead of fixed overflow, when:
The results of a conversion from decimal to binary exceeds the maximum length
allowed by the implementation.
A fixed, floating-point, or decimal divide exception is detected by the hardware, as, for
example, when using the DIVIDE built-in function and the quotient exceeds the size
specified for the result.
The syntax for ZERODIVIDE is:
ON ZERODIVIDE
If the ZERODIVIDE condition is raised and it is disabled, the program is in error.
Result: The result of a division by zero is undefined.
Implicit Action: A message is printed and the ERROR condition is raised.
Status: ZERODIVIDE is enabled throughout the program, except within the scope of a
condition prefix specifying NOZERODIVIDE.
Normal Return: Control returns to the point immediately following the point at which the
condition was raised.
Example
A=15;
B=0;
C=A/B; /* ZERODIVIDE condition

*/

Example to demonstrate various CONDITIONS


//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD FIXED DEC(5,2);
DCL BUFF CHAR(10);
DCL BUFF1 CHAR(20) INIT('ABCDEFGHIJKLMNOP');
DCL (J,K) FIXED BINARY(15);
DCL A FIXED DECIMAL(5,2);
DCL MYCONDITION CONDITION;
ON CONVERSION BEGIN;
PUT SKIP LIST('CONVERSION CONDITION RAISED');
PUT SKIP EDIT('CHAR IN ERROR IS=',ONCHAR( ))(A);
PUT SKIP EDIT('FIELD IN ERROR IS=',ONSOURCE( ))(A);
PUT SKIP LIST('ATTEMPTING REPAIR...');
ONCHAR='1';
END;
ON STRINGSIZE PUT SKIP LIST('STRINGSIZE CONDITION...');
ON STRINGRANGE PUT SKIP LIST('STRINGRANGE CONDITION...');

PL/I

23/01/2006

Page:- 59 / 119

ON ERROR PUT SKIP LIST('ERROR CONDITION RAISED, PROGRAM


ABENDING..');
ON FINISH PUT SKIP LIST('FINISH CONDITION RAISED.DO CLEANUP');
ON FIXEDOVERFLOW PUT SKIP LIST('FIXED OVERFLOW CONDITION
RAISED');
ON SIZE PUT SKIP LIST('SIZE CONDITION RAISED');
ON NAME(SYSIN) PUT SKIP LIST('NAME CONDITION RAISED ON SYSIN');
ON CONDITION(MYCONDITION) PUT SKIP LIST('MYCONDITION RAISED');
J=1;K=15;
GET EDIT (FIELD)(F(5,2)); /* raises conversion condition */
PUT EDIT(FIELD)(F(6,2));
(STRINGSIZE):BUFF=SUBSTR(BUFF1,J,K); /* raises stringsize cond */
J=10;K=20;
(STRINGRANGE):BUFF=SUBSTR(BUFF1,J,K);/*raises stringrange cond */
J=32700;K=32700;J=J*K*J; /* raises fixed overflow condition */
J,K=32700;
(SIZE):J=J+K; /* raises size condition */
GET SKIP DATA(A); /* name condition raised */
GET SKIP DATA(A); /* this time data is fine*/
(STRINGSIZE):SIGNAL STRINGSIZE; /* just for kicks */
SIGNAL STRINGSIZE; /*sorry wont work here */
SIGNAL CONDITION(MYCONDITION);
END MYPROG;
/*
//GO.SYSIN DD *
A1200
B=12.34;
A=12.34;
/*
//

PL/I

23/01/2006

SECTION 8

Page:- 60 / 119

ARRAYS

back

Arrays
DCL STUD_AVG (365) FIXED DEC(4,2);
DCL TABLE(0:11) FIXED DEC(5);
DCL GRAPH(-3:3) FIXED DEC(5,2);
DCL LIST(-2:6) FIXED BIN(15,0) INIT(1,2,4,21,2,3,4,80,90);
DCL TABLE(3,2) FIXED DEC(5);
ROW 1
ROW 2
ROW 3

COLUMN 1
(1,1)
(2,1)
(3,1)

COLUMN 2
(1,2)
(2,2)
(3,2)

DCL AXIS(-3:3,-4:4) FLOAT DEC(6) INIT((63)0);


DCL B(10) FIXED DEC(3) INIT((7)0,1,2,3);
DCL TABLE(10) CHAR(2) INIT((10)(2)'A');
DCL TABLE(10) CHAR(5) INIT((10)(1)'EMPTY');
/* note the (1) !. It is required ! */
Access an element as below AXIS(-3,-4)
Subscripts can be nested as below

/* first element)

DCL X(5) FIXED BIN(15,0) INIT(10,20,30,40,50);


DCL Y(3) FIXED BIN(15,0) INIT(3,2,1);
I=3;
Z=X(Y(I));
/*
Z is 10
*/
Variable array bounds
PROC1: PROCEDURE;
DCL
A(500) FIXED BIN(31) INIT((500)7);
DCL
B
FIXED BIN(31);
CALL MYSUM(A,B);
.
.
.
END PROC1;
MYSUM:PROCEDURE(ARRAY,SUM);
DCL ARRAY(*) FIXED BIN(31);
DCL SUM
FIXED BIN(31);
DCL LOW
FIXED BIN(31);
DCL HIGH
FIXED BIN(31);
DCL INDEX
FIXED BIN(31);
DCL (HBOUND,LBOUND) BUILTIN;
LOW = LBOUND(ARRAY,1);
/* second parm is the dimension we are looking at*/
HIGH = HBOUND(ARRAY,1);
/* LOW and HIGH are now the bounds of ARRAY*/
SUM=0;
DO INDEX=LOW TO HIGH BY 1;
SUM=SUM + ARRAY(INDEX);
END;
END MYSUM;

PL/I

23/01/2006

Page:- 61 / 119

I/O operations and Arrays


DCL AMOUNT(20) FIXED DECIMAL(3);
GET LIST (AMOUNT);
/* AMOUNT(1) to AMOUNT(20) are input */
Array Assignment
DCL MONTHS(12)
FIXED DEC(4,1);
MONTHS = 0; /* inits all elements of MONTHS to 0
DCL
DCL
A=B;

A(10)
B(10)

FIXED DEC(6);
FIXED DEC(6);

DCL

FIXED DEC(6) INIT(1,2,3,4,5,6);

A=-A;

/* A is now -1,-2,-3,-4,-5,-6

*/

A=A*2;

/* A is now -2,-4,-6,-8,-10,-12

*/

DCL
A=A+B;

*/

FIXED DEC(6) INIT(1,2,3,4,5,6);


/* A is now -1,-2,-3,-4,-5,-6

*/

Cross Sections of Arrays


DCL
TABLE(3,4)
FIXED BINARY(15);
TABLE(*,1)
refers to first column of TABLE
TABLE(1,*)
refers to first row of TABLE
TABLE(*,3)
refers to third column of TABLE
SUBSCRIPTRANGE condition
Default state is disabled. To enable do one of the following
(SUBSCRIPTRANGE):
MATRIX: PROC OPTIONS(MAIN);
.
END MATRIX;
ON SUBSCRIPTRANGE
BEGIN;
.
.
END;
Example of ARRAY usage
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL ARRAY(2,10) FIXED DECIMAL(2) INIT((10)1,(10)2);
PUT DATA (ARRAY);
PUT SKIP LIST('ARRAY AFTER ARRAY+10..');
ARRAY=ARRAY+10;
PUT SKIP;
PUT DATA (ARRAY);
PUT SKIP LIST('NOW RESTORING ARRAY...');
ARRAY(1,*)=1;
ARRAY(2,*)=2;
PUT SKIP;

PL/I

23/01/2006

Page:- 62 / 119

PUT DATA(ARRAY);
END MYPROG;
/*
//
Example to demonstrate ARRAYS
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD_ARRAY(10) FIXED DECIMAL(7,2) INIT(1,2,3,(7)9);
DCL SUM FIXED DECIMAL(7,2);
CALL ADD(FIELD_ARRAY);
PUT SKIP LIST(SUM);
ADD:PROCEDURE (A);
DCL A(*) FIXED DECIMAL(7,2);
DCL (HBOUND,LBOUND) BUILTIN;
DCL INDEX FIXED BINARY(15);
DCL UPPER FIXED BINARY(15);
DCL LOWER FIXED BINARY(15);
SUM=0;
LOWER=LBOUND(A,1);
UPPER=HBOUND(A,1);
DO INDEX=LOWER TO UPPER BY 1;
SUM=SUM+A(INDEX);
END;
END ADD;
END MYPROG;
/*
//

PL/I

23/01/2006

SECTION 9

Page:- 63 / 119

PICTURES AND STRUCTURES

back

Pictures
To treat character strings as Arithmetic data
To treat arithmetic quantities as character strings
To edit data ( Zero suppression, Dollar float, +,-,DB,CR and comma decimal
insertion in numeric data) for output to printer
PICTURE 'picture specification characters'
Picture specification characters are 9,V,$,Z,.,,,DB,CR
DCL

PIC '999V99'

DCL

PIC '(3)9V(2)9' /* V is the implied decimal point

*/

Bytes
5
5
5
5
5
5
5
5
5
5
6
6
6
6
6
6

Equivalent
Value assigned Int. value
DECIMAL FIXED(5)
12345
12345^
DECIMAL FIXED(5)
12345
12345^
DECIMAL FIXED(5,2) 123.45
123^45
DECIMAL FIXED(5,2) 12345
345^00
DECIMAL FIXED(5,5) 12345
^00000
DECIMAL FIXED(5)
123
00123^
DECIMAL FIXED(5,2) 123
123^00
DECIMAL FIXED(2,1) 123.45
3^4
DECIMAL FIXED(5,2) -123.45
123^45
DECIMAL FIXED(5,2) -123.45
-123^45
DECIMAL FIXED(5,2) -123.45
-123^45
DECIMAL FIXED(5,2) +123.45
123^45
DECIMAL FIXED(5,2) +123.45
+123^45
DECIMAL FIXED(5,2) -123.45
123^45DECIMAL FIXED(5,2) +123.45
+123^45
DECIMAL FIXED(5,2) -123.45
123^45

Note

PIC
99999
99999V
999V99
999V99
V99999
99999
999V99
9V9
999V99
S999V99
-999V99
-999V99
S999V99
999V99S
+999V99
+999V99

/* 9 represents a numeric digit */

Note:
1:Truncation of most significant digits occurred
2:Truncation of significant digits occurred
3:Truncation on both sides of decimal points occurred
4:Sign is lost as picture clause did not have provision for sign S
Arithmetic Operations on Decimal Picture Data
DCL
SUM PIC'9999';
DCL
A
PIC'999';
DCL
B
PIC'999';
SUM = A + B;
The compiler generates code to
a)Convert A to Fixed Decimal
b)Convert B to Fixed Decimal format
c) Add A and B
d)Convert the result to character form (PIC of SUM)
e)Place the converted result in SUM.

1
2
3
4

PL/I

23/01/2006

Page:- 64 / 119

Note that arithmetic operations can be performed on PIC fields with editing
characters. However it results in inefficient code.
Z picture character Is used for suppression of leading Zeroes.
PIC
ZZZZ9
ZZZZ9
ZZZZZ
ZZZV99
ZZZVZZ
ZZZVZZ
ZZZV99
Z9999
ZZZVZ9
ZZ9ZZ

Value assigned
Internal Representation
100
bb100
0
bbbb0
0
bbbbb
123
12300
1234
23400
.01
bbbb1
0
bbb00
0
b0000
/* invalid. If one Z appears to right of decimal, then all edit
chars must b Z */
/* Invalid. All Z'S must be to the left of the 9
*/

Decimal Point
This is an insertion character
DCL
A
PIC'999V.99' INIT(12.34);
PUT LIST(A); /* outputs 012.34

*/

DCL
A
PIC'999V99' INIT(12.34);
PUT LIST(A); /* outputs 01234
*/
The alignment is caused by the V edit character. Decimal point is only output as
an insertion character. See example below to illustrate this:
DCL
A
PUT LIST(A);

PIC'999.99V' INIT(12.34);
/* outputs
000.12 */

Comma
This is an insertion character
DCL
A
PIC'9,999V.99' INIT(3512.34);
PUT LIST(A); /* outputs 3,512.34

*/

DCL
A
PUT LIST(A);

PIC'Z,ZZZV.99' INIT(3512.34);
/* outputs 3,512.34
*/

DCL
A
PUT LIST(A);

PIC'Z,ZZZV.99' INIT(512.34);
/* outputs
512.34

*/

Blank
Is another insertion character. Use this to generate blanks on the right hand side
of the picture string. If you need blanks on the left, use the Z edit character
DCL
DCL
DCL

A
PIC'999V99BBB';
/* three blanks on the right*/
B
PIC'Z,ZZZV.99(7)B';
/* seven blanks on the right*/
EDITED_DATE PIC'99B99B99'; /*insert blanks between
YY,MM, and DD*/

Slash
Is another insertion character
DCL
RUN_DATE
CHAR(6);
DCL
EDITED_RUN_DATE PIC'99/99/99';
EDITED_RUN_DATE=RUN_DATE;

PL/I

23/01/2006

Page:- 65 / 119

Dollar Sign
In the Floating form (where there is more than one $ sign) leading zeroes
are suppressed and last leading Zero is replaced with the $ sign. In static
form (only one $ sign) it appears wherever defined in the picture string.
DCL
B
PUT LIST(B);

PIC'$999V.99' INIT(12.34);
/* outputs $012.34

*/

DCL
B
PUT LIST(B);

PIC'$$$$V.99' INIT(12.34);
/* outputs b$12.34

*/

Sign characters (S,-,+)


The above characters may be also drifting.
DCL
A
PIC'S999'
INIT(12);
PUT LIST(A); /*
outputs
+012 */
DCL
A
PIC'SSS9'
INIT(12);
PUT LIST(A); /*
outputs
+12
*/
DCL
A
PIC'9999S'
INIT(1234);
PUT LIST(A); /*
outputs
1234+ */
DCL
A
PIC'+99'
INIT(144);
PUT LIST(A); /*
outputs
+44
*/
DCL
A
PIC'999V.99S' INIT(-123.45);
PUT LIST(A); /*
outputs
123.45- */

/*error!*/

Asterisk
Usually used as a floating character for protection against forgery
DCL
A
PIC'*****9V.99'
INIT(104.75);
PUT LIST(A); /*
outputs
***104.75
*/
DCL
A
PIC'*****V.**'
INIT(104.75);
PUT LIST(A); /*
outputs
**104.75
*/
DCL
A
PIC'*****V.**'
INIT(.75);
PUT LIST(A); /*
outputs
*****.75 */
CR and DR
DB or CR can be used to indicate negative values. CR or DB
to the right of all digit positions in the PIC clause.
DCL
A
PIC'99V.99CR'
INIT(-12.75);
PUT LIST(A); /*
outputs
12.75CR
DCL
A
PIC'99V.99DB'
INIT(-12.75);
PUT LIST(A); /*
outputs
12.75DB
DCL
A
PIC'99V.99CR'
INIT(12.75);
PUT LIST(A); /*
outputs 12.75bb

can only appear


*/
*/
*/

Character String Pictures


These cannot be used for data items which are to participate in computations .
They are made up of A, X, and 9
Built-in functions of SUBSTR, LENGTH, INDEX and VERIFY can be used on
these data items.
A
specifies characters A to Z or blank
9
specifies a numeric character
X
can contain any character
The B and other insertion characters may not be specified in these PIC clauses
This type of PIC attribute is used primarily for data validation
Example:
Assume we need to ensure that any data item is of the form 1237AB

PL/I

23/01/2006

Page:- 66 / 119

The first 4 positions are numeric and the last two are alphabetic.
Code the data item which is to receive the above data as PIC'9999AA'
DCL
A
PIC'9999AA';
DCL
B
CHAR(6);
A = B; /* if the format of the assigned data does not conform to the
PIC of the receiving field, the CONVERSION condition
is raised
*/
The P specification is allowed in input and output GET and PUT statements
GET FILE(SYSIN) EDIT(A,B,C,D) (COL(1),P'ZZZ9',P'99V99',P'AA999',P'(5)9');
Input value
bb15
1234
AB123
AB123

Format
P'ZZZ9'
P'99V99'
P'AA999'
P'(5)9'

Resulting internal value


0015
12^34
AB123
conversion error !!

DCL
ASSETS
FIXED DECIMAL(11,2);
ASSETS = 45326985.76;
PUT EDIT(ASSETS) (P'$$$$,$$$,$$$V.99');
/* outputs b$45,326,985.76
*/
DCL
ASSETS
FIXED DECIMAL(11,2);
ASSETS = 2500.00;
PUT EDIT(ASSETS) (P'$ZZZ,ZZZ,ZZZV.99');
/* outputs $bbbbbb2,500.00 */
Structures
DCL

EMP_ADDRESS,
2
NAME CHAR(20),
2
STREET
CHAR(20),
2
CITY
CHAR(20),
2
STATE
CHAR(20);
READ FILE(INFILE) INTO (EMP_ADDRESS);
Notes:
Level 1 is the Major structure level which does not have attributes. However
other storage qualifiers like BASED(P) are to be specified here.
Any number > 1 can represent lower levels
Attributes and INIT are defined only at elementary levels
There is no equivalent to the COBOL FILLER
STRING(EMP_ADDRESS) concatenates all the elements into one character
string.
STRING(structure variable) can also be used as a pseudo variable (on the
LHS of an assignment statement.

Initial Attribute
Elementary data items in the structure can have the INIT attribute
Example:
DCL
1
SALARY_RECORD,
2
NAME,
3
LAST
CHAR(10)
INIT('JOHNSON'),
2
EMP_NO
FIXED DEC(5) INIT(12345),
2
HOURS,
3
REGULAR FIXED DEC(4) INIT(100),

PL/I

23/01/2006
2
3

WAGES,
REG_PAY FIXED DEC(4)

Page:- 67 / 119

INIT(2000);

Names within a structure


DCL
1
EMP_REC,
3
REGULAR_PAY PIC'999V99',
3
HOURS,
5
REGULAR
PIC'99',
5
OVERTIME
PIC'99',
3
WAGES,
5
REGULAR
PIC'99V99',
5
OVERTIME
PIC'99V99';
EMP_REC.REGULAR_PAY = EMP_REC.HOURS.REGULAR *
EMP_REC.WAGES.REGULAR;
REGULAR_PAY = HOURS.REGULAR * WAGES.REGULAR;
/* also OK if the second and third qualifier uniquely identify the data item */
LIKE
DCL 1 MYOBJDESC LIKE MQOD;
Arrays in structures
DCL
1
2
2
2

INVENTORY_ITEM,
PART_NO
CHAR(8),
QTY_IN_HAND
PIC'9999',
SALES_HISTORY(12) PIC'99999';

INVENTORY_ITEM.SALES_HISTORY(1) = 1234;
Arrays of structures
DCL
1
2

WEATHER(20),
TEMP,
3
HIGH FIXED DEC(4,1),
3
LOW FIXED DEC(4,1),
2
VELOCITY,
3
HIGH FIXED DEC(4,1),
3
LOW FIXED DEC(4,1),
2
RAINFALL,
3
HIGH FIXED DEC(4,1),
3
LOW FIXED DEC(4,1);
Refer to structure as WEATHER(n)
Refer to VELOCITY as VELOCITY(n)
Refer to TEMP.HIGH as TEMP.HIGH(n) /* called subscripted qualified name
*/

Assignments
We can assign one structure to another so long as they have the same minor
structuring and same number of elementary items. If arrays are contained within,
the bounds must be the same. We can assign Major to minor structures or vice
versa provided the relative structuring, as defined above, is the same.
Note that even if the attributes of the data items are different, conversion takes
place as per rules.
DCL 1 A

PL/I

23/01/2006
2
2
2
2
2

Page:- 68 / 119

B FIXED DEC(5),
C FIXED BIN(31),
D CHAR(20),
E FIXED BIN(15),
F FLOAT DEC(5);

DCL 1 AA
2 BB FIXED BIN(15),
2 CC FIXED DEC(5),
2 DD CHAR(10),
2 EE FIXED DEC(4),
2 FF FIXED DEC(6);
A = AA;

/* valid assignment. Conversions occur


/* equivalent to
*/
/*
B = BB
*/
/*
C = CC
*/
/*
D = DD
*/
/*
E = EE
*/
/*
F = FF
*/

Defining Overlay of a structure on a structure


Example
DCL 1 ISSUES,
2 CODE
CHAR(1),
2 QTY
PIC '9999',
2 JOB_NO
CHAR(4),
2 PART_NO CHAR(7),
2 DEPT
CHAR(3),
2 FILLER1
CHAR(3);
DCL 1 RECEIPTS DEFINED ISSUES,
2 CODE
CHAR(1),
2 QTY
PIC '9999',
2 PART_NO CHAR(7),
2 SUPPLIER CHAR(6);
READ FILE(INPUT) INTO ISSUES;
SELECT (ISSUES.CODE);
WHEN ('1') CALL PROCESS_ISSUES;
WHEN ('2') CALL PROCESS_RECEIPTS;
OTHERWISE CALL CODE_IN_ERROR;
END;

*/

PL/I

23/01/2006

SECTION 10

Page:- 69 / 119

STORAGE CONTROL

back

Storage Classes
Unless declared otherwise variables will have the storage class AUTOMATIC
MAIN: PROCEDURE OPTIONS(MAIN);
DCL 1 STRUCTURE,
2 A FIXED DEC (6,2),
2 B CHAR(20);
.
.
P1:
PROC;
DCL TABLE(100) CHAR(10);
.
END P1;
P2:
PROC;
DCL LIST(500) FIXED;
.
END P2;
END MAIN;
MAIN
P1
P2
STRUCTURE
TABLE

AUTOMATIC Storage

The above is the layout when P1 is called


MAIN
P1
P2
STRUCTURE
LIST

AUTOMATIC Storage

The above is the layout when P2 is called


Note the overlaying of TABLE and LIST which is characteristic of AUTOMATIC variables
Automatic variables lifetime is the lifetime of the enclosing block (Either a PROCEDURE
block or BEGIN block).
Automatic variables make efficient use of storage. The storage for an automatic variable
is allocated on entry into the block by the prologue code and de-allocated on exit from
the block by the epilogue code generated by the compiler.
STATIC Storage
MAIN: PROCEDURE OPTIONS(MAIN);
DCL 1 STRUCTURE,
2 A FIXED DEC (6,2),
2 B CHAR(20);
.
.
P1:
PROC;
DCL TABLE(100) CHAR(10) STATIC;
END P1;

PL/I

23/01/2006

Page:- 70 / 119

P2:

PROC;
DCL LIST(500) FIXED STATIC;
.
END P2;
END MAIN;
MAIN
P1
P2
LIST
TABLE
STRUCTURE

static storage
static storage
automatic storage

The above is the layout when P1 or P2 are called


Note that Static storage is allocated before start of program execution and remains
allocated throughout program execution.
Where you want variables to retain their value from one invocation of the procedure to
another declare them with the static attribute.
Static variables can be initialised just like automatic variables
DCL FLAG
BIT(1) INIT('1'B) STATIC;
Based Storage
DCL P POINTER;
DCL A(100) FIXED DEC(5) BASED(P);
/* first declaration of P is not mandatory as second declaration
implies the nature of P which is a variable of type POINTER
Note that storage for A is NOT allocated by the above declaration */
To refer variable A one of the following needs to be done
Assign to P the value returned by an ADDR( ) function
DCL B(100) FIXED DEC(5);
P = ADDR(B);
Assign to P another pointer which has a valid value
DCL B(100) FIXED DEC(5);
DCL Q POINTER
Q = ADDR(B);
P=Q;
Initialise P by using it with SET option of a READ I/O statement
READ FILE(INPUT) SET(P);
/* I/O read direct to based variable */
Allocate the variable which P is tied to.
DCL (P,Q) POINTER;
DCL AREA CHAR(100) BASED(P);
ALLOCATE(AREA);
/* First generation of AREA */
ALLOCATE(AREA) SET(Q); /* Second generation of AREA */
AREA = ABCD;
/* First generation used */

PL/I

23/01/2006
Q->AREA = PQRS;

Page:- 71 / 119

/* Second generation used */

Implementing Overlays
DCL A(100) FIXED BIN(15);
DCL B(50) FIXED BIN(15) DEFINED A;
/* traditional definition of overlay
*/
DCL A(100) FIXED BIN(15);
DCL B(50) FIXED BIN(15) BASED(P);
P = ADDR(A);
/* alternate method
*/
/* note that storage size of B must not
exceed that of A
*/
It is possible to have two variables based on one POINTER.
DCL PTR POINTER;
DCL 1 A,
2 B PIC'9999',
2 C FIXED DEC (13,2),
2 D CHAR(21);
DCL 1 J
2
2
2

BASED(PTR),
X FIXED BIN(15),
Y FLOAT DEC(6),
Z BIT(7);

DCL 1 W BASED(PTR),
2 K FIXED BIN(15),
2 L FIXED BIN(15);
PTR = ADDR(A);
When overlay defining a CHAR varying field special considerations exist
DCL FIELD CHAR(100) VARYING;
DCL 1 STRUCTURE BASED(P),
2 LENGTH FIXED BIN(15,0),
2 DATA CHAR(100);
P = ADDR(FIELD);
Note that LENGTH overlays the Half word length field preceding the varying
character field.
Using POINTERS in I/O (Locate mode I/O)
DCL P POINTER;
DCL TAPE FILE INPUT RECORD ENVIRONMENT(F BLKSIZE (240)
RECSIZE(24));
DCL 1 ISSUES BASED(P),
2 CODE
CHAR(1),
2 QTY
PIC'(4)9',
2 JOB_#
PIC'(4)9',
2 PART_#
PIC'(7)9',
2 DEPT
PIC'99',
2 UNUSED
CHAR(6);
DCL 1 RECEIPTS BASED(P),
2 CODE
CHAR(1),
2 QTY
PIC'(4)9',
2 UNUSED
CHAR(6),

PL/I

23/01/2006

Page:- 72 / 119

2 PART_#
PIC'(7)9',
2 SUPPLIER CHAR'(6)9';
READ FILE (TAPE) SET(P);
SELECT (ISSUES.CODE);
WHEN ('1') CALL PROCESS_ISSUES;
WHEN ('2') CALL PROCESS_RECEIPTS;
END;
/* using locate mode output */
DCL DATA BASED(P);
NEXT: LOCATE DATA FILE(OUTFILE);
DATA = ...;
GO TO NEXT;
Controlled Storage
DCL A(100) FIXED DEC(5) CONTROLLED; /* A does not exist here */
ALLOCATE A; /* Storage for A is allocated here
*/
GET LIST(A);
. . . use A here
FREE A;
/* Storage for A is de-allocated here */
Note that controlled variables after allocation exist until they are explicitly freed. They
are not affected by block boundaries. For arrays the size specification may be deferred
until ALLOCATE
DCL A(*) FIXED DEC(5) CONTROLLED;
ALLOCATE A(100);
DCL TABLE(*,*) BIN(1) CONTROLLED;
ALLOCATE TABLE(100,100);
Note that a repeat allocation before a free results in a new generation of the variable. The
previous generation is pushed into a stack meant for storing multiple generations of
controlled variables. A free will then cause the previous generation to be popped out of
the stack. Example follows:
DCL BUFF CHAR(100) CONTROLLED;
ALLOCATE BUFF;
BUFF = 'THIS IS FIRST GENERATION OF BUFF';
PUT LIST(BUFF);
/* this is first generation of buff */
ALLOCATE BUFF;
BUFF = 'THIS IS SECOND GENERATION OF BUFF';
PUT LIST(BUFF);
/* this is second generation of buff*/
FREE BUFF;
PUT LIST(BUFF);
/* this is first generation of buff */
FREE BUFF;
/* No BUFF in existence
*/
The ALLOCATION(X) built in function returns a FIXED BINARY(31) value indicating the
number of generations of X that exist.
DCL 1 PRODUCT BASED(Q),
2 DESCRIPTION CHAR(20),

PL/I

23/01/2006
2

CODE

Page:- 73 / 119

FIXED DEC(4);

DCL P POINTER;
ALLOCATE PRODUCT SET(P); /* note that Q is not changed by this */
P->PRODUCT.DESCRIPTION = 'SCREW';
/* PRODUCT is referenced by a locator qualifier */
FREE P->PRODUCT;
DCL NULL BUILTIN;

/* FREE the storage for the variable this way*/


/* NULL is a builtin function which returns a NULL
pointer */

Storage Control
AUTOMATIC specifies that storage is allocated upon each entry to the block that
contains the storage declaration. The storage is released when the block is exited.
STATIC specifies that storage is allocated when the program is loaded. The storage is
not freed until program execution is completed.
CONTROLLED specifies that you maintain full control over the allocation and freeing
of storage with the ALLOCATE and FREE statements. Multiple allocations of the same
controlled variable in the same task, without intervening freeing, stack generations of
the variable.
BASED, like CONTROLLED, specifies that you maintain full control over storage
allocation and freeing. Multiple allocations are not stacked but are available at any
time. Each allocation can be identified by the value of a locator variable.
The default storage class is AUTOMATIC
Automatic and based variables can have internal scope only.
Static and controlled variables can have internal or external scope.
Static Storage and Attribute
You use static storage when the variable is local to the procedure and the value it
contains must be saved between successive invocations. Variables declared with the
STATIC attribute are allocated prior to running a program. They remain allocated until the
program terminates
Example
A: PROC OPTIONS(MAIN);
.
.
.
B: PROC;
DECLARE X STATIC INTERNAL;
.
.
.
END B;
END A;
Although the variable X is allocated throughout the program, it can be referenced only
within procedure B or any block contained in B.

PL/I

23/01/2006

Page:- 74 / 119

Automatic Storage and Attribute


Automatic variables are allocated on entry to the block in which they are declared. They
can be reallocated many times during the execution of a program. You control their
allocation by your design of the block structure. The syntax for the AUTOMATIC attribute
is:
DCL X FIXED BIN(31) AUTOMATIC;
Example
A: PROC;
.
.
.
CALL B;
B:PROC;
DECLARE (X,Y) AUTO;
.
.
.
END B;
.
.
.
CALL B;
Each time procedure B is invoked, the variables X and Y are allocated storage. when B
terminates the storage is released, and the values they contain are lost.
Array bounds, string lengths, and area sizes for automatic variables can be specified as
expressions.
A:PROC;
DECLARE N FIXED BIN;
.
.
.
B:PROC;
DECLARE STR CHAR(N);
Controlled Storage and Attribute
Variables declared as CONTROLLED are allocated only when they are specified in an
ALLOCATE statement. You have individual control over each controlled variable. a
controlled variable remains allocated until a FREE statement that names the variable is
encountered or until the end of the program in which it is allocated.
Controlled variables are independent of the program block structure.
Example
A: PROC;
DCL X CONTROLLED;
CALL B;
.
.
.
B:PROC;
ALLOCATE X;

PL/I

23/01/2006

Page:- 75 / 119

.
.
.
END B;
END A;
The variable X can be referred to within the procedure B and that part of the procedure A
that follows execution of the CALL statement.
ALLOCATE Statement for Controlled Variables
The ALLOCATE statement allocates storage for controlled variables, independent of
procedure block boundaries. The bounds of controlled arrays, the lengths of controlled
strings, and the size of controlled areas, as well as their initial values, can also be
specified at the time the ALLOCATE statement is executed.
If a bound, length, or size is explicitly specified in an ALLOCATE statement, it
overrides that given in the DECLARE statement.
If a bound, length, or size is specified by an asterisk in an ALLOCATE statement, the
bound, length, or size is taken from the current generation.
DCL X (20) FIXED BIN CTL;
ALLOCATE X; /* the upper bound is taken as 20 from the DCL statement */
DCL X(20) CHAR(5) CONTROLLED;
ALLOCATE X(25) CHAR(6);
/* DCL upper bound and length are overridden

*/

Initial values are assigned to a variable upon allocation, if it has an INITIAL attribute in
either the ALLOCATE statement or DECLARE statement. If an INITIAL attribute
appears in both DECLARE and ALLOCATE statements, the INITIAL attribute in the
ALLOCATE statement is used.
FREE Statement for Controlled Variables
The FREE statement frees the storage allocated for controlled variables. The storage can
then be used for other allocations. For controlled variables, the next most recent
allocation is made available, and subsequent references in the task refer to that
allocation.
Multiple Generations of Controlled Variables
An ALLOCATE statement for a variable for which storage was previously allocated and
not freed pushes down or stacks storage for the variable. This stacking creates a new
generation of data for the variable. The new generation becomes the current generation;
the previous generation cannot be directly accessed until the current generation has
been freed. When storage for this variable is freed, using the FREE statement, storage is
popped up from the stack.
Example
DCL X(10,20) CHAR(5) CTL;
ALLOCATE X;
ALLOCATE X(10,10);
ALLOCATE X(*,*);
The first generation of X has bounds (10,20); the second and third generations have
bounds (10,10). The elements of each generation of X are all character strings of length
5.

PL/I

23/01/2006

Page:- 76 / 119

The asterisk notation can also be used in a DECLARE statement, but has a different
meaning.
Example
DCL Y CHAR(*) CTL,
N FIXED BIN;
N=20;
ALLOCATE Y CHAR(N);
ALLOCATE Y;
The length of the character string Y is taken from the previous generation unless it is
specified in an ALLOCATE statement, in which case Y is given the specified length. This
allows you to defer the specification of the string length until the actual allocation of
storage.
Built-in Functions for Controlled Variables
The ALLOCATION built-in function returns a binary value of precision (31,0) indicating
the number of generations that you can access in the current task for a given controlled
variable. Array-handling functions DIM, which determines the extent of a specified
dimension of an array, and LBOUND and HBOUND, which determine the lower and
upper bound, respectively, of a specified dimension of
a given array. The
CURRENTSTORAGE built-in function return the amount of storage required by a
particular variable. STORAGE returns allocated storage for a variable. For strings, the
built-in function LENGTH returns the current length of the string.
Based Storage and Attribute
A declaration of a based variable is the amount of storage required and its attributes. A
locator value identifies the location of the generation. A based variable can be used to
describe existing data, to obtain storage by means of the ALLOCATE statement, or to
access data in a buffer by means of the LOCATE statement or READ (with SET option)
statement.
DCL X FIXED BIN BASED(P);
This declares that references to X, except when the reference is explicitly qualified, use
the variable P to locate the storage for X.
Locator Data
There are two types of locator data: pointer and offset.
The value of a pointer variable is effectively an address of a location in storage
relative to the start of the virtual address space ( X00000000).
The value of an offset variable specifies a location relative to the start of an AREA
variable and remains valid when the area is assigned to a different part of storage.
When an offset variable is used in a reference, its value is implicitly converted to a
pointer value.
Explicit conversion of an offset to a pointer value is accomplished using the POINTER
built-in function.
DCL P POINTER, O OFFSET(B), B AREA;
P = POINTER(O,B);
The OFFSET built-in function complements the POINTER built-in function and returns
an offset value derived from a given pointer and area.

PL/I

23/01/2006

Page:- 77 / 119

Locator Qualification
Locator qualification is the association of one or more locator references with a based
reference to identify a particular generation of a based variable. This is called a locatorqualified reference. The composite symbol -> represents qualified by or points to.
Example:
P -> X
X is a based variable and P is a locator variable.
Reference to a based variable can also be implicitly qualified. The locator reference used
to determine the generation of a based variable that is implicitly qualified is the one
declared with the based variable.
Example
DCL X FIXED BIN BASED(P);
ALLOCATE X;
X= X + 1;
References to X can also be explicitly locator-qualified as follows:
P->X = P->X + 1;
Q = P;
Q->X = Q->X + 1;
Levels of Locator Qualification
DECLARE X BASED (P),
P POINTER BASED (Q),
Q OFFSET (A);
ALLOCATE P; /* have to do this first to make instance of P */
ALLOCATE X; /* Now we can make an instance of X */
The references: X, P->X, and Q->P->X all represent three levels of locator
qualification and are equivalent ways of referencing X.
POINTER Variable and Attribute
A pointer variable is declared contextually if it appears in the declaration of a based
variable, as a locator qualifier, in a BASED attribute, or in the SET option of an
ALLOCATE, LOCATE, or READ statement.
Setting Pointer Variables
NULL ( ) returns null pointer value
SYSNULL ( ) returns null pointer value. Use for static pointer and offset variables
POINTERADD(X,Y) . X is a pointer, Y is a FIXED BIN(31)
POINTERVALUE(X). X is FIXED BIN(31)
POINTER(X,Y). X is an OFFSET , Y is an AREA, returns a POINTER.
A READ statement with the SET option.
An ALLOCATE statement.
By assignment of the value of another locator variable, or a locator value returned by
a user-defined function.
Note: BINARYVALUE(P) is the reverse of POINTERVALUE(X). P is a pointer and the
function returns a FIXED BINARY(31) VALUE.
Built-In Functions for Based Variables

PL/I

23/01/2006

Page:- 78 / 119

The ADDR built-in function returns a pointer value that identifies the first byte of a
variable.
In general, the value of the NULL built-in function is used whenever a pointer (or offset)
variable does not identify a location in storage. There are many ways a pointer can
acquire the null value:
1. By assignment of the NULL built-in function
2. Assignment of the value returned by the ADDR built-in function for an unallocated
controlled variable.
3. It can also acquire the system null value by the assignment of the SYSNULL built-in
function.
ALLOCATE Statement for Based Variables
The ALLOCATE statement allocates storage for based variables and sets a locator
variable that can be used to identify the location, independent of procedure block
boundaries.
ALLOCATE (based-variable [, based-variable,])
[IN ( area-reference)]
[SET (locator-reference)]

IN :-Specifies the area variable in which the storage is allocated.


SET :- Specifies a locator variable that is set to the location of the storage allocated.
If the SET option is not specified, the declaration of the based variable must specify a
locator variable.

Storage is allocated in an area when the IN option is specified or the SET option specifies
an offset variable.
FREE Statement for based variables
The FREE statement frees the storage allocated for based and controlled variables.
FREE locator-qualifier->based-variable [IN (area-reference)]
Area Data and Attribute
Area Variables describe areas of storage that are reserved for the allocation of based
variables. This reserved storage can be allocated to, and freed from, based variables by
the ALLOCATE and FREE statements. Area variables can have any storage class and
must be aligned.
When a based variable is allocated and an area is not specified, the storage is obtained
from wherever it is available.
You might want to identify the locations of based variables within an area variable relative
to the start of the area variable. Offset variables are provided for this purpose.
A variable is given the AREA attribute contextually by its appearance in the OFFSET
attribute or an IN option, or by explicit declaration. The syntax for the AREA attribute is:
AREA [ ( * | expression ) ]
If expression, or *, is not specified, the default is 1000
Use * for AREA with controlled attribute to defer the area size until allocation.
Examples of AREA declarations are:

PL/I

23/01/2006

Page:- 79 / 119

DECLARE AREA1 AREA (2000),


AREA2 AREA;
Offset Data and Attribute
Offset data is used exclusively with area variables. The value of an offset variable
indicates the location of a based variable within an area variable relative to the start of
the area
DCL variable-name OFFSET (area-variable)
Setting Offset Variables
The value of an offset variable can be set in any one of the following ways:
By an ALLOCATE statement
By assignment of the value of another locator variable, or a locator value returned by
a user-defined function.
By assignment of the NULL built-in function value
By using the return value from the OFFSET built-in function.
Example:
DCL X
Y
A
O

BASED(O),
BASED(P),
AREA,
OFFSET(A);

ALLOCATE X;
ALLOCATE Y IN(A);
The storage class of area A and offset O is AUTOMATIC by default. The first ALLOCATE
statement is equivalent to:
ALLOCATE X IN(A) SET(O);
The second ALLOCATE statement is equivalent to;
ALLOCATE Y IN(A) SET(P);
Area Assignment
The value of an area reference can be assigned to one or more area variables by an
assignment statement. Area-to-area assignment has the effect of freeing all allocations in
the target area and then assigning the extent of the source area to the target area, so
that all offsets for the source area are valid for the target area.
Example
DCL X BASED (O(1)),
O(2) OFFSET (A),
(A,B) AREA;
ALLOC X IN (A);
X = 1;
ALLOC X IN (A) SET (O(2));
O(2)->X = 2;
B = A;
Using the POINTER built-in function, the references POINTER (O(2),B)->X and

PL/I

23/01/2006

Page:- 80 / 119

O(2)->X represent the same value allocated in areas B and A respectively.


INITIAL Attribute
Example
DCL NAME CHAR(10) INIT(JOHN DOE);
DCL PI FIXED DEC (5,4) INIT (3.1416);
DECLARE X INIT(SQRT(Z));
DECLARE A INIT ((B*C));
DCL C CHAR(3) STATIC INIT (('A'| | 'BC'))
DCL X(10) FIXED BINARY(31) INIT((10)0);
DCL X(10) FIXED BINARY(31) INIT((5)0,(2)1,(3)2);
DCL X(10) FIXED BINARY(31) INIT(0,1,,2,3,,,5);
DCL TABLE (20,20) INITIAL CALL SET_UP(X,Y);
DCL A(15) CHAR(13) INITIAL( 'JOHN DOE', *,'RICHARD ROW', 'MARY SMITH');
Example
((2) 'A')
((2) ('A'))
((2) (1)'A')

is equivalent to ('AA')
is equivalent to ('A','A')
is equivalent to ('A','A')

Note that first index is string repetition factor ( (3)'B' is 'BBB'). The outer index is the
iteration factor ( (2)(3)'B' is 'BBB','BBB').
Example of demonstration of POINTERS
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
%PROCESS ATTRIBUTES(FULL),NOT('~');
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL 1 LINK BASED(P),
2 DATA CHAR(80),
2 NEXT POINTER;
DCL (HEAD,THIS) POINTER;
DCL COUNT FIXED DEC(3);
DCL BUFFER CHAR(80);
DCL NULL BUILTIN;
ALLOCATE LINK SET(HEAD);
COUNT=1;
PUT STRING(BUFFER) EDIT(COUNT,'ABCDEFGHIJKLMNOPQRS')(F(3),A);
HEAD->DATA=BUFFER;
THIS=HEAD;
DO I=1 TO 100 BY 1;
ALLOCATE LINK SET(THIS->NEXT);
COUNT=COUNT+1;
PUT STRING(BUFFER) EDIT(COUNT,'ABCDEFGHIJKLMNOPQRS')(F(3),A);
THIS=THIS->NEXT;
THIS->DATA=BUFFER;
END;
THIS->NEXT=NULL;
THIS=HEAD;
DO WHILE(THIS ~= NULL);
PUT SKIP EDIT(THIS->DATA)(A);
THIS=THIS->NEXT;
END;

PL/I

23/01/2006

Page:- 81 / 119

END MYPROG;
/*
//
Example of demonstration of CONTROLLED attribute
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
%PROCESS ATTRIBUTES(FULL),NOT('~');
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL 1 LINK CONTROLLED,
2 DATA CHAR(80);
DCL COUNT FIXED BINARY(15);
DCL BUFFER CHAR(80);
DCL NULL BUILTIN;
DO I=1 TO 100 BY 1;
ALLOCATE LINK;
PUT STRING(BUFFER) EDIT(I,'ABCDEFGHIJKLMNOPQRS')(F(3),A);
LINK.DATA=BUFFER;
COUNT=COUNT+1;
END;
COUNT=ALLOCATION(LINK);
DO I=1 TO COUNT BY 1;
PUT SKIP EDIT(LINK.DATA)(A);
FREE LINK;
END;
END MYPROG;
/*
//

PL/I

23/01/2006

SECTION 11

Page:- 82 / 119

FILE PROCESSING GENERAL

back

TYPES OF TRANSMISSION
Stream Data formatting facilities, less efficient, conversion to character form before
output or input. Stream implies that the data of the file is a continuos stream of
data items in character form assigned to variables or from expressions to the
stream
Record No data formatting facilities, more efficient, image of data in program buffer
written to media. Record mode implies that the file consists of physically separate
records each of which consist of one or more data items in any form
Record VS Stream I/O
STREAM I/O
(LIST / EDIT / DATA)
GET characters

Convert to
Coded arithmetic
form

RECORD
READ

Process coded
arithmetic form

Process Data in
Record form
Any
conversions
have to be
done by the
programmer

Convert coded
arithmetic form
to character form

Write

Put characters

Comparison between the two forms of I/O


In Stream I/O conversions take place in the process of reading the data from
Character to coded form or vice versa. No such conversions take place with record I/O
In stream I/O less than or more than one physical record may be needed to satisfy a
GET or a PUT. In record I/O only one record is output or input at a time for a READ or
WRITE statement.
In Stream I/O the programmer knows in advance the format of the data (else it is not
possible to specify the data items or format list). In record I/O programmer needs to
know only RECSIZE / BLKSIZE. Data format need not be known.

PL/I

23/01/2006

Page:- 83 / 119

In record I/O data in any form (coded or character) may be stored. In stream form the
data has to be in character form only.
Record mode I/O may be used with any type of data set(QSAM, VSAM KSDS, VSAM
RRDS, VSAM ESDS). Stream I/O can be used only on sequential data sets.
TYPES OF DATA SETS SUPPORTED BY PL/I
Type of data set
PL/I organization
Sequential
CONSECUTIVE
Indexed sequential
INDEXED
Direct
REGIONAL
KSDS,ESDS,RRDS
VSAM
FILE INPUT / OUTPUT
Action
Define the File
Open the File
Process the File
Close the File

PL/I statement
DECLARE filename FILE
OPEN FILE(filename);
READ/WRITE/REWRITE/DELETE/LOCATE for access by
record
GET/PUT for stream access
CLOSE FILE(filename)

A file used within a PL/I program has a PL/I file name. If you have the following file
declaration in your program:
DCL STOCK FILE STREAM INPUT;
Create a DD statement with a data definition name (DDNAME)
//STOCK

DD

DSN=PARTS.INSTOCK, . . .

Ensure that the DDNAME of the DD statement that defines the data set is the same as
either the declared PL/I file name. If the file name is longer than 7 characters, the default
DDNAME is composed of a compiler generated truncated name (first 4 and last 3
characters).
FILE ATTRIBUTE OF A DATA ITEM
Main attributes are
GROUP
ALTERNATIVES
Usage
STREAM | RECORD
Function
INPUT | OUTPUT | UPDATE
Access
SEQUENTIAL | DIRECT
Scope
EXTERNAL | INTERNAL
Notes on Attributes
RECORD
STREAM

DEFAULT
STREAM
INPUT
SEQUENTIAL
EXTERNAL

Associated verbs OPEN, CLOSE, READ, WRITE,


REWRITE, DELETE, LOCATE
Associated verbs OPEN, CLOSE, GET, PUT

INPUT
OUTPUT
UPDATE

Input only
Output only
Valid only for RECORD mode. Transmission in either
direction

SEQUENTIAL

Valid in RECORD MODE only. Records accessed in

PL/I

23/01/2006

DIRECT

Page:- 84 / 119

physical sequence
Valid in RECORD MODE only. Records accessed in any
order by KEY. Implies KEYED attribute.

ENVIRONMENT

VSAM | CONSECUTIVE
F | FB | V | VB, BLKSIZE(N), RECSIZE (N)

KEYED

Only RECORD files. It means records can be


accessed using one of KEY, KEYTO, KEYFROM of data
transmission statements

Input / Output Verbs


OPEN FILE (file-reference)
[STREAM | RECORD]
[INPUT | OUTPUT | UPDATE]
[DIRECT | SEQUENTIAL]
[KEYED]
[PRINT]
[TITLE(expression)]
[LINESIZE(expression)]
[PAGESIZE(expression)]
GET
PUT

FILE(file-reference).
FILE(file-reference).

/*both*/
/*use only one*/
/*UPDATE for record only*/
/*record only */
/*record only */
/*stream only*/
/*both */
/*stream output only*/
/*stream only with PRINT*/
/*Stream I/O*/
/*Stream I/O*/

READ FILE(file-reference)..
WRITE FILE(file-reference)
REWRITE FILE(file-reference).
DELETE FILE(file-reference)

/*Record I/O*/
/*Record I/O*/
/*Record I/O*/
/*Record I/O*/

CLOSE FILE(file-reference)

/*Both forms I/O*/

PL/I

23/01/2006

Page:- 85 / 119

SECTION 12

STREAM ORIENTED DATA TRANSMISSION


back
Data sets with the STREAM attribute are processed by stream-oriented data
transmission, which allows your PL/I program to ignore block and record boundaries and
treat a data set as a continuous stream of data values in character form.
For output, PL/I converts the data items from program variables into character form if
necessary, and builds the stream of characters into records for transmission to the data
set.
For input, PL/I takes records from the data set and separates them into the data items
requested by your program, converting them into the appropriate form for assignment to
program variables.
Defining Files using Stream I/O
DCL filename FILE STREAM {INPUT | {OUTPUT [PRINT]}}
ENVIRONMENT (options);
Options CONSECUTIVE

F | FB | FS | FBS | V |VB | U
RECSIZE (record-length)
BLKSIZE (block-size)

Default Definitions created by the PL/I compiler


DCL SYSPRINT FILE STREAM OUTPUT PRINT ENV(V BLKSIZE(129));
(120 characters of data, 1 ASA character , 4 bytes Block size, 4 Bytes Record size)
DCL SYSIN FILE STREAM INPUT ENV (F BLKSIZE(80));
In your JCL include the following statements to use SYSPRINT and SYSIN
//SYSPRINT DD SYSOUT=A
//SYSIN

/*

DD

Use the GET and PUT verbs for the I/O within your program
Salient points of Stream I/O
The I/O is in a continuos stream and record boundaries are not reflected to the user
program, although they exist at the Physical level.
The I/O is in character mode. For example coded arithmetic data is converted to
character (display) mode both ways(From arithmetic mode to character mode on
output and from character mode to coded mode on input).
On input the data items must be in character mode and either BLANK or COMMA
must be used as a separator (LIST mode).
In PUT LIST for printer output the data is output in predetermined tab positions
spaced at 21 positions between tabs.
List Directed data Transmission

PL/I

23/01/2006

Page:- 86 / 119

Data Directed Data Transmission


Edit Directed Data Transmission
Verbs
OPEN .
/* see previous section
*/
CLOSE
/* see previous section
*/
GET FILE(file-reference) data-specification [SKIP (expression)]
PUT FILE(file-reference) data-specification [PAGE] [LINE(expression)]
[SKIP(expression)]
PUT STRING(char-reference) data-specification

/* SPRINTF of C */

Data Specifications
LIST (data-item, data-item,.)
DATA (data-item, data-item,.)
EDIT (data-item, data-item,) (format-item, format-item,)
Notes
COPY

Specifies that source data stream is written without alteration on the


specified data set
GET FILE(SYSIN) DATA(A,B,C) COPY(DPL);

SKIP

Specifies a new current line (or record ) in the data set;


PUT LIST (X,Y,Z) SKIP(3);
outputs on SYSPRINT after skipping three lines

PAGE

Only for PRINT files. Starts a new page.

LINE

Only for PRINT files. Changes line counter value.

Example
DCL INVEN FILE INPUT STREAM ENV(options);
ENV options are BLKSIZE, RECSIZE etc. Note that these are required for new files
and may be specified in the program or in the JCL. For existing files these can be
omitted as the system will fill up the DCB from the catalogue information for the
existing dataset.
Note that Filename is the DD name in the run JCL for this program.
INPUT or OUTPUT attribute can be deferred and specified with the OPEN statement
also. This can be useful when the same file is opened for write / closed / opened for
read.
Default is INPUT unless the file has the PRINT attribute in which case it defaults to
OUTPUT.
Example
DCL INVEN FILE STREAM INPUT ENV(F BLKSIZE(80));
Print Attribute
Use this for printer output.
This will enable options on PUT like PAGE, SKIP( ), LINE( ) etc.

PL/I

23/01/2006

Page:- 87 / 119

This attribute can be used only when STREAM and OUTPUT are other attributes.
This attribute causes first byte of each record of the data set to be reserved for
carriage control character
Note record size is size of print line plus one position for RECFM F. For RECFM V it is
print line size + 9.
DCL PRINTR FILE OUTPUT STREAM PRINT ENV(F BLKSIZE(133));
DCL PRINTBUF CHAR(132);
PUT FILE(PRINTR) LIST(PRINTBUF);
DCL I FIXED BINARY(15);
I=LINENO(PRINTR) /*returns value of the LINENO counter maintained by PL/I */
Stream Files are automatically opened with first GET or PUT.
PAGESIZE and LINESIZE for PRINT files should be specified only in OPEN statement.
OPEN FILE(PRINTR) PAGESIZE(50) LINESIZE(120);
Default output is to SYSPRINT with default line size of 120.Override this by
OPEN FILE(SYSPRINT) LINESIZE(133);
Default input is from SYSIN with a RECSIZE of 80
Note that FILE attributes can be specified in the JCL, DCL and OPEN statements. They
are merged at OPEN time.
The CLOSE FILE(DATAIN); statement is used to close the file. Program end also
automatically closes all files
Edit Directed I/O
Edit Directed I/O overcomes some of the limitations of LIST directed I/O like need for
spaces or commas to separate data (wastage of space) or printing at predetermined
tab positions for printer output.
Edit I/O is part of Stream I/O and therefore deals only in character data.
Consider the following Data which is intended to be read with LIST directed I/O
GET LIST(EMPNUM,EMPNAME);
EMPNUM EMPNAM
12345678,SRINIVASKAMATH
Note the comma separating the two data items
For EDIT directed I/O
GET FILE(filename) EDIT(datalist) (formatlist);
GET EDIT(EMPNUM,EMPNAME) (COLUMN(1),F(8),A(15));
EMPNUM EMPNAM
12345678SRINIVASKAMATH
Note the absence of blank or comma to separate the two data items
Format identifiers are
A(n)
n Alphameric characters

PL/I

23/01/2006
F(m,n)
X(n)
COLUMN(n)

Page:- 88 / 119

Fixed point with the precision of m,n


Skip n positions
Data starts from column n

Note that in the event of insufficient format identifiers the set of identifiers provided are
used again starting with the first format identifier
GET EDIT(A,B,C) (F(4),F(5));
A
->
F(4)
B
->
F(5)
C
->
F(4)
Excess format identifiers are ignored.
The I/O continues across record boundaries until all the identifiers have been
read or written
A
B
1

70 71 80 1

30

DCL
A
CHAR(70);
DCL
B
CHAR(40);
GET EDIT (A,B) (A(70),A(40));
A
1

Record boundary
B

70

40

DCL
A
CHAR(70);
DCL
B
CHAR(40);
Record boundary
GET EDIT (A,B) (COLUMN(1),A(70),COLUMN(1),A(40));
Alternately,
DCL
A
CHAR(70);
DCL
B
CHAR(40);
GET EDIT (A,B) (A(70),X(10),A(40));
Example:
To read first forty positions of each record
DCL FIELD_1 CHAR(40);
DO I=1 to 10;
GET EDIT(FIELD_1) (COLUMN(1),A(40));
END;
Data items may be Data aggregates
Example:
DCL
TABLE(100)
FLOAT DEC(6);
GET EDIT(TABLE) (COLUMN(1),F(6,2));
/* note that 100 records will be read to satisfy this read
I/O can be to a string in memory rather than to a FILE
PUT STRING (FIELD_1) EDIT (A,B,C) (A(20),A(10),F(3));
GET STRING (FIELD_1) EDIT (A,B,C) (A(20),A(10),F(3));

*/

PL/I

23/01/2006

Page:- 89 / 119

This option can be used to effect CHAR to coded arithmetic or vice versa conversion.
Writing Headings
PUT EDIT('THIS IS A HEADING STARTING IN COLUMN 32')
(PAGE,COLUMN(32),A); /*the compiler calculates width of A*/
PUT EDIT(MM,'/',DD,'/',YY) (A,A,A,A,A);
Format Identifiers
A(w)
Character field of width w
A

Character field of width determined by the data item it represents

B(w)

Bit field of width w

Bit field of width determined by the data item it represents

F(w)

Field contains w characters containing a fixed point decimal value.


123
F(3)
123
-123
F(3)
SIZE error
-123
F(4)
-123
123
F(5)
bb123

F(w,d)

Width of character field is w and d positions are to right of decimal


point.
123.45
F(4,0)
b123
123.45
F(6,2)
123.45
123.45
F(7,3)
123.450
123.45
F(6,1)
b123.5
123.45
F(5,2)
SIZE error
123.65
F(4,0)
b124

X(w)

Skip w characters in input or output

Picture format characters $ + - Z 9 v / , . B.


Used to edit numeric data in fixed decimal point format.

COLUMN(n)

Start from Column n

LINE(n)

Skip to line n

PAGE

Skip to new page (output only)

SKIP

On input continue with next record


On output SKIP(n) where n=0 suppress line feed, n=1 print on next line,
n = expression where n-1 lines are skipped.

Data Directed Input


Each item in the input is in the form of an assignment statement that specifies both value
and the identity of the variable to which it is to be assigned.
Example:
The input stream could contain
A=12.13,B=570,C='SED',D='1011'B;

PL/I

23/01/2006

Page:- 90 / 119

GET DATA(A,B,C,D);
GET DATA(B,D,A,C);

/* would work just as well

*/

GET DATA(A,B,C,D,E); /* not an error. E is superfluous and is ignored

*/

GET DATA(A,C,D); /*ERROR!. Identifier in input stream but not in ET*/


/* this raises the NAME condition which can be handled */
/* by an ON NAME(SYSIN)
*/
GET DATA;

/* all items in stream up to the semicolon are input. However


the data names must have been defined prior to this point */

The COUNT(filename) returns the data items input during last GET
Data Directed Output
DCL
A
FIXED DEC(5) INIT(0);
DCL
B
FIXED DEC(5) INIT(0);
DCL
C
FIXED DEC(5) INIT(175);
PUT DATA (A,B,C);
Outputs ->
A=0
B=0
C=175;
Note that A is in TAB position 1
B is in TAB position 25
C is in TAB position 49
On Units and relevant built in functions
UNDEFINEDFILE(file reference) condition is raised when the file referenced fails to open.
You can use
ENDFILE(file reference) is raised on sequential input, when a read is issued after the end
of file is reached.
The TRANSMIT(file reference) is raised whenever a block / record I/O failed. Usually due
to media problems, typically tape I/O.
ENDPAGE(file reference) is raised on end of a page
NAME(file reference) is raised when a GET DATA finds superfluous data in the input
stream. Use the DATAFIELD( ) built in function to find the field in error.
KEY(file reference) is raised for keyed files (VSAM KSDS for example) when a keywrror
occurs.
LINENO(file reference) built in function to find out the line number you are currently at on
a page in a print file.
COUNT(file reference) returns the number of items input or output on the last GET or
PUT operation.
ONFILE( ) returns the file referenced when a conversion condition is entered due to a
conversion condition on a GET or PUT operation.
ONKEY( ) returns the key in error.

PL/I

23/01/2006

Example of stream I/O


//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP
EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD CHAR(20) INIT('HELLO WORLD');
PUT LIST (FIELD);
GET LIST (FIELD);
PUT SKIP LIST(FIELD);
PUT SKIP EDIT(FIELD,FIELD) (A(8),X(1),A(1));
END MYPROG;
/*
//GO.SYSIN DD *
'SRINIVAS KAMATH'
/*
//
Example of stream I/O
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP
EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD1 FIXED DECIMAL(7,2);
DCL FIELD2 FIXED DECIMAL(7,2);
DCL SUM FIXED DECIMAL(7,2);
GET LIST (FIELD1,FIELD2);
SUM=FIELD1+FIELD2;
PUT SKIP LIST(SUM);
END MYPROG;
/*
//GO.SYSIN DD *
123.4 567.8
/*
//
Example of stream I/O
//USERAA1 JOB MSGCLASS=A,NOTIFY=USERAA
//MYSTEP
EXEC PROC=IEL1CLG,REGION.PLI=1M
//PLI.SYSIN DD *
MYPROG: PROCEDURE OPTIONS(MAIN);
DCL FIELD1 FIXED DECIMAL(7,2);
DCL FIELD2 FIXED DECIMAL(7,2);
DCL SUM FIXED DECIMAL(7,2);
GET DATA (FIELD1,FIELD2);
SUM=FIELD1+FIELD2;
PUT SKIP LIST(SUM);
END MYPROG;
/*
//GO.SYSIN DD *
FIELD1=1.23 FIELD2=3.45;
/*
//

Page:- 91 / 119

PL/I

23/01/2006

Page:- 92 / 119

Controlling Printed Line length


1. You can limit the length of the printed line produced by a PRINT file either by
specifying a record length in your PL/I program (ENVIRONMENT attribute) or via JCL
DCB parameter.
2. By giving a line size in an OPEN statement (LINESIZE option). The record length
must include the extra byte for the print control character, that is, it must be 1 byte
larger than the length of the printed line (9 bytes larger for V-format records).The
value you specify in the LINESIZE option refers to the number of characters in the
printed line; the compiler adds the print control character.
Example of creating a print file Via Stream Data Transmission
%PROCESS NOT('~');
CARDPRNT: PROC OPTIONS (MAIN);
DCL TABLE FILE STREAM OUTPUT PRINT;
DCL PGNO
FIXED DEC(2) INIT(1);
DCL ONCODE
BUILTIN;
DCL EOF BIT(1) INIT('0'B);
DCL CARD CHAR(80);
ON ENDFILE(SYSIN) EOF INIT('1'B);
ON ERROR BEGIN;
ON ERROR SYSTEM;
PUT LIST ('ONCODE = '|| ONCODE);
END;
ON ENDPAGE (TABLE)BEGIN;
PUT FILE (TABLE) PAGE
EDIT (CARD TO PRINT PGNO= ,PGNO) (A,F(2));
PGNO = PGNO +1;
END;
OPEN FILE (TABLE) PAGESIZE(52) LINESIZE (80);
SIGNAL ENDPAGE(TABLE);
GET LIST(CARD);
DO WHILE ~EOF;
PUT FILE (TABLE) EDIT(CARD)(A);
GET LIST(CARD);
END;
END CARDPRNT;

PL/I

23/01/2006

SECTION 13

Page:- 93 / 119

RECORD I/O

back

Define Files (DASD or TAPE ). I/O is record based.


Use READ and WRITE (instead of GET and PUT)

DCL file-constant FILE RECORD {INPUT | OUTPUT | UPDATE}


{SEQUENTIAL | DIRECT} [KEYED]
ENVIRONMENT( CONSECUTIVE | VSAM
RECSIZE(n)
BLKSIZE(n)
F | FB | V | VB . . .)
Notes:Only the minimum commonly used attributes have been shown above
If you code DIRECT , KEYED is also required.
You code DIRECT and KEYED for VSAM KSDS / RRDS files
If you code VSAM in the ENV option, the RECSIZE, BLKSIZE and record format are not
applicable.
If you are defining a PS file (CONSECUTIVE), you do not need to code RECSIZE ,
BLKSIZE and record format for an input file.
If you are creating a new PS file the record format, BLKSIZE and RECSIZE must be
coded either in the DCL statement or in the JCL.
VSAM files must be created using IDCAMS before they can be used from your PL/I
program. PS files may be created by your program when it executes.
Verbs for Record Oriented Data Transmission only
READ FILE(file-reference) [INTO(reference) [ KEY(expression) | KEYTO(reference) ]]
READ FILE(file-reference)
[SET(pointer-reference) [KEY(expression) | KEYTO(reference) ] ]
WRITE FILE (file-reference) FROM(reference)
[KEYFROM(expression) | KEYTO(reference) ]]
DELETE FILE(file-reference) [KEY(expression)]
LOCATE based-variable FILE(file-reference)
REWRITE FILE (file reference) FROM(reference) [ KEY(expression)]
Notes
INTO

Specifies Data area in form of element or aggregate variable into which


the logical record is read.

FROM

Specifies the element or aggregate from which the record is written.

SET

Specifies a pointer variable that is set to point to the location in the buffer
into which the record has been moved during the READ operation

PL/I

23/01/2006

Page:- 94 / 119

KEY

Specifies character key that identifies a record for a VSAM KSDS data
set. Can appear only if the FILE has DIRECT attribute or VSAM and
KEYED attribute. Can be used in a READ statement for INPUT or
UPDATE file or in a REWRITE for a DIRECT UPDATE file.

KEYFROM

Specifies a character key that identifies the record in the data set. Can
be used in a WRITE statement for SEQUENTIAL output, or WRITE
statement for DIRECT OUTPUT to a VSAM data set.

KEYTO

Specifies the character variable to which the key of a record is assigned.


Variables with PIC and STRING cannot be used. Applies only to KEYED
VSAM.
For VSAM KSDS the recorded key padded or truncated on right (if
required) is moved
For ESDS a 4 character RBA padded or truncated on right (if required
is moved)
For VSAM RRDS a 8 character relative record number padded or
truncated on the left is moved

Write Sequences
Sequential or Direct keyed Write
WRITE FILE(LOANS) FROM (LOANREC) KEYFROM(LOANNO);
Sequential non-keyed Write
WRITE FILE(LOANS) FROM (LOANREC);
Locate mode sequential write
DCL BUFFER CHAR(80) BASED(P);
LOCATE BUFFER FILE(OUTFILE);
Locate mode keyed write
DCL BUFFER CHAR(80) BASED(P);
DCL KEYFLD CHAR(5);
LOCATE BUFFER FILE(OUTFILE) KEYFROM(KEYFLD);
Read Sequences
Sequential Read
DCL BUFFER CHAR(80);
READ FILE(INFILE) INTO(BUFFER);
Sequential Read Locate mode
DCL BUFFER CHAR(80) BASED(P);
READ FILE(INFILE) SET(P);
Direct Read
DCL BUFFER CHAR(80);
DCL KEYFLD CHAR(5);
READ FILE(INFILE) INTO(BUFFER) KEY(KEYFLD);
Direct Read Locate Mode
DCL BUFFER CHAR(80) BASED(P);
DCL KEYFLD CHAR(5);
READ FILE(INFILE) SET(P) KEY(KEYFLD);
Update Sequences

PL/I

23/01/2006

Page:- 95 / 119

Direct keyed update(update no matter what existing record contains)


REWRITE FILE(DIREC) FROM(OUTREC) KEY(NAME);
Direct keyed delete(delete no matter what existing record contains)
DELETE FILE(DIREC) KEY(NAME);
Update after reading the record
READ FILE(UPDTFIL) INTO(BUFFER) [KEY(KEYFLD)];
. . . modify buffer here
REWRITE FILE(UPDTFIL) FROM(BUFFER);
Delete after reading the record
READ FILE(UPDTFIL) INTO(BUFFER);
. . . check buffer here to determine whether delete is needed
DELETE FILE(UPDTFIL); /* KEY parameter is optional in this sequence */
Example for PS file:
DCL INFILE FILE INPUT RECORD ENV(F RECSIZE(80));
DCL OUTFILE FILE OUTPUT RECORD ENV(F RECSIZE(80));
DCL DATA_AREA CHAR(80);
OPEN
FILE(INFILE),FILE(OUTFILE);
.
READ FILE(INFILE) INTO (DATA_AREA);
WRITE FILE(OUTFILE) FROM(DATA_AREA);
.
.
CLOSE FILE(INFILE),FILE(OUTFILE);
On units and conditions for record I/O
UNDEFINEDFILE(file reference) is driven when the file referenced fails to open.
You can use
KEY(file reference) is raised for keyed files (VSAM) whenever you get a duplicate key
condition on output, a record not found on input, or record out of sequence for sequential
keyed write. The built in function ONKEY( ) can be used to return the key in error.
ENDFILE(file reference) is raised on sequential input, when a read is issued after the end
of file is reached.
The RECORD(file reference) condition can be raised due to mismatch between the
record length(s) as seen by the program and as defined for the file.
The TRANSMIT(file reference) is raised whenever a block / record I/O failed. Usually due
to media problems, typically tape I/O.

PL/I

23/01/2006

Page:- 96 / 119

SECTION 14

DEFINING AND USING VSAM DATA SETS


back
Before you execute a program that accesses a VSAM data set, you need to know:
The name of the VSAM data set
The name of the PL/I file
For example, if your file is named PL1FILE, your data set named VSAMDS, the dd
statement must be coded as below:
// PL1FILE

DD

DSNAME=VSAMDS, DISP=SHR

PL/l provides support for three types of VSAM data sets:


Key-sequenced data sets (KSDS)
Entry-sequenced data sets (ESDS)
Relative record data sets (RRDS).
Before using a VSAM data set for the first time, you need to define it to the system with
the DEFINE command of Access Method Services.
Use the three different types of data sets according to the following purposes:
Use entry-sequenced data sets for data that you primarily access in the order in which
it was created.
Use key-sequenced data sets when you normally access records through keys within
the records (for example, a stock-control file where the part number is used to access
a record).
Use relative record data sets for data in which each item has a particular number.
you can access records in all types of VSAM data sets either directly by means of a key,
or sequentially.
You can also use a combination of the two ways: Select a starting point with a key and
then read forward or backward from that point.
Keys for Indexed VSAM Data Sets
Keys for key-sequenced data sets and for entry-sequenced data sets accessed via an
alternate index are part of the logical records recorded on the data set. You define the
length and location of the keys when you create the data set. You can reference the keys
in the KEY, KEYFROM, and KEYTO options
Relative Byte Addresses (RBA)
Relative byte addresses allow you to use keyed access on an ESDS associated with a
KEY SEQUENTIAL file. The RBA, or keys, are character strings of length 4, and their
values are defined by VSAM. You cannot construct or manipulate RBA in PL/l; obtain the
RBA for a record by using the KEYTO option, on a WRITE or on a READ statement.
Subsequently use an RBA in the KEY option of a READ or REWRITE statement.
Relative Record Numbers
Records in an RRDS are identified by a relative record number that starts at 1 and is
incremented by 1 for each succeeding record. Keys used as relative record numbers are
character strings of length 8. Source key you use in the KEY or KEYFROM option must

PL/I

23/01/2006

Page:- 97 / 119

represent an unsigned integer. If not 8 characters truncate or pad with blanks (interpreted
as Zeros) on the left.
You define a direct VSAM data set by using a file declaration with the following attributes:
DCL filename FILE RECORD {INPUT | OUTPUT | UPDATE}
DIRECT KEYED
ENVIRONMENT (VSAM);
Loading a KSDS
Open the file for KEYED SEQUENTIAL OUTPUT and present the records in
ascending key order, use the KEYFROM option.
If the KSDS already contains some records, and you open the associated file with the
SEQUENTIAL and OUTPUT attributes, you can only add records at the end of the
data set.
KEYED SEQUENTIAL OUTPUT file is used with a WRITE with FROM and
KEYFROM keywords in the statement. The data must be presented in ascending key
order.
EXAMPLE OF DEFINING A KSDS USING IDCAMS
//USERIDB JOB CLASS=C,
//
MSGCLASS=X,MSGLEVEL=(1,1),NOTIFY=USERID
//STEP1 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE USERID.KSDS CLUSTER
DEFINE CLUSTER (NAME(USERID.KSDS) INDEXED KEYS(5,0) RECORDSIZE(80,80) TRACKS(2,2)) DATA(CONTROLINTERVALSIZE(1024))
//
Example of loading a VSAM KSDS sequentially
//USERID1 JOB NOTIFY=&SYSUID,CLASS=A,MSGLEVEL=(1,1)
//PLIPROC JCLLIB ORDER=(USERID.PLI.PROCLIB)
//DOPROC EXEC PROC=IBMZCB
//PLI.SYSIN DD DSN=USERID.PLI.SOURCE(VSAMLS),DISP=SHR
//BIND.SYSLMOD DD DSN=USERID.PLI.LOADLIB(VSAMLS),DISP=SHR
//*
//RUN EXEC PGM=VSAMLS
//STEPLIB DD DSN=USERID.PLI.LOADLIB,DISP=SHR
//VSAM
DD DSN=USERID.KSDS,DISP=SHR
//IN
DD DSN=USERID.PLI.LOAD.DATA,DISP=SHR
//SYSPRINT DD SYSOUT=*
//
Input data presented via USERID.PLI.LOAD.DATA (PS,FB,80/800)
0000100010SDSDSDSDSSDSDSD
0000200011SDSDSDSDSSDSDSD
0000400012SDSDSDSDSSDSDSD
0000600010SDSDSDSDSSDSDSD
0001800011SDSDSDSDSSDSDSD
0002000012SDSDSDSDSSDSDSD
0003200010SDSDSDSDSSDSDSD
0004400014SDSDSDSDSSDSDSD
0005600011SDSDSDSDSSDSDSD

PL/I

23/01/2006

Page:- 98 / 119

0006800010SDSDSDSDSSDSDSD
VSAMLS
*PROCESS NOT('~');
VSAMLS: PROC OPTIONS(MAIN);
DCL (ONFILE,ONCODE) BUILTIN;
DCL VSAM FILE RECORD SEQUENTIAL OUTPUT KEYED ENV(VSAM);
DCL IN FILE STREAM INPUT;
DCL BUFFER CHAR(80);
DCL KEYFLD CHAR(5) DEF (BUFFER);
DCL EOF BIT(1) INIT('0'B);
ON UNDEFINEDFILE(IN) BEGIN;
PUT SKIP LIST('OPEN ERROR ',ONFILE);
STOP;
END;
ON UNDEFINEDFILE(VSAM) BEGIN;
PUT SKIP LIST('OPEN ERROR ',ONFILE);
STOP;
END;
ON KEY(VSAM) BEGIN;
PUT SKIP EDIT('KEY ERROR ON FILE ',ONFILE,
' ONCODE ',ONCODE)(A);
END;
ON ENDFILE(IN) BEGIN;
EOF='1'B;
END;
OPEN FILE(IN);OPEN FILE(VSAM);
GET FILE(IN) EDIT(BUFFER)(A(80));
DO WHILE(~EOF);
WRITE FILE(VSAM) FROM(BUFFER) KEYFROM(KEYFLD);
GET FILE(IN) EDIT(BUFFER)(A(80));
END;
END VSAMLS;
VSAMRS
*PROCESS NOT('~');
VSAMRS: PROC OPTIONS(MAIN);
DCL (ONFILE,ONCODE) BUILTIN;
DCL VSAM FILE RECORD SEQUENTIAL INPUT KEYED ENV(VSAM);
DCL BUFFER CHAR(80);
DCL KEYFLD CHAR(5);
DCL EOF BIT(1) INIT('0'B);
ON UNDEFINEDFILE(VSAM) BEGIN;
PUT SKIP LIST('OPEN ERROR ',ONFILE);
STOP;
END;
ON ENDFILE(VSAM) BEGIN;
EOF='1'B;
END;
OPEN FILE(VSAM);
READ FILE(VSAM) INTO(BUFFER) KEYTO(KEYFLD);
DO WHILE(~EOF);
PUT SKIP EDIT(BUFFER,' ',KEYFLD)(A);
READ FILE(VSAM) INTO(BUFFER) KEYTO(KEYFLD);
END;
END VSAMRS;

PL/I

23/01/2006

Page:- 99 / 119

VSAPUPD
This program reads update information from userid.PLI.UPDT.DATA(PS,FB,81/810).
The format of the update data is
<op char(1)><key char(5)><data char(75) optional depending on code>
Code can be A for add a new record, C for change an existing record, D for deleting an
existing record and P for printing an existing record. If you code A or C the third field of
75 characters is required.
Here is sample update data
D10001
A1000110010SDSDSDSDSSDS
P10001
D10001
P10001
A1000110010SDSDSDSDSSDS
P10001
C1000110010SDSDSDSDSSDS*****updated*********
P10001
Here is the JCL
//USERID1 JOB NOTIFY=&SYSUID,CLASS=A,MSGLEVEL=(1,1)
//PLIPROC JCLLIB ORDER=(USERID.PLI.PROCLIB)
//DOPROC EXEC PROC=IBMZCB
//PLI.SYSIN DD DSN=USERID.PLI.SOURCE(VSAMUPD),DISP=SHR
//BIND.SYSLMOD DD DSN=USERID.PLI.LOADLIB(VSAMUPD),DISP=SHR
//*
//RUN EXEC PGM=VSAMUPD
//STEPLIB DD DSN=USERID.PLI.LOADLIB,DISP=SHR
//VSAM
DD DSN=USERID.KSDS,DISP=SHR
//UPDT
DD DSN=USERID.PLI.UPDT.DATA,DISP=SHR
//SYSPRINT DD SYSOUT=*
//
*PROCESS NOT('~');
VSAMUPD: PROC OPTIONS(MAIN);
DCL (ONFILE,ONCODE,ONKEY) BUILTIN;
DCL VSAM FILE RECORD DIRECT UPDATE KEYED ENV(VSAM);
DCL UPDT FILE RECORD SEQUENTIAL INPUT ENV(CONSECUTIVE);
/*
IO BUFFER DEFS
*/
DCL BUFFER CHAR(81);
DCL OP CHAR(1) DEF(BUFFER);
DCL KEYFLD CHAR(5) DEF(BUFFER) POS(2);
DCL DATA CHAR(80) DEF(BUFFER) POS(2);
/*
IO BUFFER DEFS END
*/
/*
ON UNITS DEFINITIONS START
*/
DCL EOF BIT(1) INIT('0'B);
DCL GOODKEY BIT(1);
ON ENDFILE(UPDT) EOF='1'B;
ON UNDEFINEDFILE(VSAM) BEGIN;
PUT SKIP LIST('OPEN ERROR ',ONFILE);

PL/I

23/01/2006

Page:- 100 / 119

STOP;
END;
ON UNDEFINEDFILE(UPDT) BEGIN;
PUT SKIP LIST('OPEN ERROR ',ONFILE);
STOP;
END;
ON KEY(VSAM) BEGIN;
PUT SKIP EDIT
('KEY CONDITION ',ONFILE,ONKEY)(X(3),A,X(1),A,X(1),A);
GOODKEY='0'B;
END;
/*
ON UNIT DEFINITIONS END
*/
OPEN FILE(VSAM);OPEN FILE(UPDT);
READ FILE(UPDT) INTO(BUFFER); /* PRIMING READ */
DO WHILE(~EOF);
GOODKEY='1'B;
PUT SKIP EDIT('>>>',BUFFER)(A);
SELECT (OP);
WHEN ('A') WRITE FILE(VSAM) FROM(DATA) KEYFROM(KEYFLD);
WHEN ('D') DELETE FILE(VSAM) KEY(KEYFLD);
WHEN ('C') DO;
READ FILE(VSAM) INTO(OLDDATA) KEY(KEYFLD);
IF GOODKEY
THEN REWRITE FILE(VSAM) FROM(DATA) KEY(KEYFLD);
END;
WHEN ('P') DO;
READ FILE(VSAM) INTO(DATA) KEY(KEYFLD);
IF GOODKEY THEN PUT SKIP EDIT(DATA)(X(3),A);
END;
OTHERWISE PUT SKIP LIST('BAD OPERATION CODE');
END;
READ FILE(UPDT) INTO(BUFFER);
END;
END VSAMUPD;
Creating an Alternate Index
//USERIDB JOB CLASS=C,
//
MSGCLASS=X,MSGLEVEL=(1,1),NOTIFY=USERID
//STEP1 EXEC PGM=IDCAMS,REGION=512K
//SYSPRINT DD SYSOUT=*
//SYSIN
DD *
DELETE USERID.KSDS.ALTINDX ALTERNATEINDEX
DEFINE ALTERNATEINDEX (NAME(USERID.KSDS.ALTINDX)NONUNIQUEKEY KEYS(5 5) RECORDSIZE(80 512)TRACKS(1 1)RELATE(USERID.KSDS))
/*
//
Building the Index
//KAM0001B JOB (SCB-SCB-KAM0001-XXX-999),CLASS=C,

PL/I

23/01/2006

Page:- 101 / 119

//
MSGCLASS=X,MSGLEVEL=(1,1),NOTIFY=KAM0001
//STEP1 EXEC PGM=IDCAMS,REGION=512K
//SYSPRINT DD SYSOUT=*
//DD1
DD DSN=KAM0001.KSDS,DISP=OLD
//DD2
DD DSN=KAM0001.KSDS.ALTINDX,DISP=OLD
//SYSIN
DD *
BLDINDEX INFILE(DD1) OUTFILE(DD2)
/*
//
Defining the path
//USERIDB JOB CLASS=C,
//
MSGCLASS=X,MSGLEVEL=(1,1),NOTIFY=USERID
//STEP1 EXEC PGM=IDCAMS,REGION=512K
//SYSPRINT DD SYSOUT=*
//SYSIN
DD *
DEFINE PATH(NAME(USERID.KSDS.PATH)PATHENTRY(USERID.KSDS.ALTINDX))
/*
//
You can use the VSAMRS program to read the KSDS file via the PATH
//USERID1 JOB NOTIFY=&SYSUID,CLASS=A,MSGLEVEL=(1,1)
//PLIPROC JCLLIB ORDER=(USERID.PLI.PROCLIB)
//DOPROC EXEC PROC=IBMZCB
//PLI.SYSIN DD DSN=USERID.PLI.SOURCE(VSAMRS),DISP=SHR
//BIND.SYSLMOD DD DSN=USERID.PLI.LOADLIB(VSAMRS),DISP=SHR
//*
//RUN EXEC PGM=VSAMRS
//STEPLIB DD DSN=USERID.PLI.LOADLIB,DISP=SHR
//VSAM
DD DSN=USERID.KSDS.PATH,DISP=SHR
//SYSPRINT DD SYSOUT=*
//

PL/I

SECTION 15

23/01/2006

Page:- 102 / 119

PL/I and DB2

back

SQLCA
You must include the SQLCA.
EXEC SQL
INCLUDE SQLCA;
Coding rules
1. Note how you code the embedded SQL statement as illustrated above.
2. The statement conforms to a PL/I source statement rules ( except that you must specify
EXEC
3. SQL on one line) and is terminated with a semi colon.
4. You can include PL/I comments in embedded SQL statements wherever you can use a blank,
except between the keywords EXEC and SQL
5. You can use any valid PL/I name for a host variable.
6. For the WHENEVER Statement, the target for the GOTO clause must be a label in the PL/I
source code and must be within the scope of any SQL statements that WHENEVER affects.
7. EXEC SQL WHENEVER SQLERROR GOTO :Host Label
8. You must explicitly declare all host variable before their first use in the SQL statements,
unless you specify the pre-compiler option TWOPASS.
9. A colon (:) must precede all host variables in an SQL statement.
10. Where the variable declaration is not complete, the pre-compiler uses the data attribute
defaults specified in the statement PL/I DEFAULT. If the declaration for a variable is not valid,
then any SQL statement that references the variable might result in the message
"UNDECLARED HOST VARIABLE".
11. The pre-compiler uses only the names and data attributes of the variables; it ignores the
alignment, scope, and storage attributes
12. You can use a PL/I host structure name so long as the subordinate levels name scalars.
13. Use a FIXED BINARY(15) variable as an indicator variable. The usage is as in other
languages:- :<variable name that can have NULL values >:<indicator variable name>
DB2 extensions, Result Set Locators
DCL ( variable-name, . . .) SQL TYPE IS RESULT_SET_LOCATOR
The pre-compiler defines these as FIXED BINARY(31) even though it will ultimately have a
pointer value. If you want to assign a pointer variable to this field you must use the built in
function BINARYVALUE to convert the pointer to a FIXED BINARY(31) value.
SQLDA
Under some circumstances, based on your application, you may have to include an SQL
descriptor area. Some instances where you may have to do this are indicated below:FETCH...USING DESCRIPTOR descriptor-name

PL/I

23/01/2006

Page:- 103 / 119

Where instead of specifying host variables, you set up the SQLDA for receiving a row from the
result set
DESCRIBE statement-name INTO descriptor-name
Where a prepared statement is described (information about the result row in terms of columns is
set into the SQLDA)
DESCRIBE CURSOR INTO descriptor-name
The information returned in the SQLDA describes the columns in the result set associated with
the named cursor.
DESCRIBE PROCEDURE INTO descriptor-name
You use this when you dont know how many result sets are being returned by a stored
procedure.
CALL...USING DESCRIPTOR descriptor-name
Where the SQLDA that contains a valid description of host variables that are to be passed as
parameters to the procedure.
Mapping DB2 data types to PL/I data types. Note that an even SQLTYPE value , say 500 allows
nulls and 501 does not allow nulls. Note that float data types are also available, see the DB2
manuals.
PL/I Data Type

SQLTYPE of
Host Variable

SQLLEN of Host
Variable

SQL Data Type

BIN FIXED(n) 1<=n<=15

500

SMALLINT

BIN FIXED(n) 16<=n<=31

496

INTEGER

DEC FIXED(p,s)
0<=p<=15 and
0<=s<=p(1)

484

p in byte 1, s in
byte 2

DECIMAL(p,s)

CHAR(n)

452

CHAR(n)

CHAR(n) VARYING
1<=n<=254

448

VARCHAR(n)

CHAR(n) VARYING n>254

456

VARCHAR(n)

SQL TYPE IS
RESULT_SET_LOCATOR

972

Result set locator(2)

PL/I

23/01/2006

Page:- 104 / 119

SQL Data Type

PL/I Equivalent

Notes

SMALLINT

BIN FIXED(n)

1<=n<=15

INTEGER

BIN FIXED(n)

16<=n<=31

DECIMAL(p,s) or
NUMERIC(p,s)

If p<16:
DEC FIXED(p) or
DEC FIXED(p,s)

p is precision;
s is scale.
1<=p<=31
and 0<=s<=p

REAL or
FLOAT(n)

BIN FLOAT(p) or
DEC FLOAT(m)

1<=n<=21,
1<=p<=21 and
1<=m<=6

DOUBLE PRECISION
DOUBLE, or
FLOAT(n)

BIN FLOAT(p) or
DEC FLOAT(m)

22<=n<=53,
22<=p<=53 and
7<=m<=16

CHAR(n)

CHAR(n)

1<=n<=254

VARCHAR(n) or
LONG VARCHAR

CHAR(n) VAR

For LONG VARCHAR


columns, run DCLGEN
against the table to
determine the length
that DB2 assigns to the
column. Use that length
to choose an appropriate
value for n.

DATE

CHAR(n)

n=>10

TIME

CHAR(n)

n=>8

TIMESTAMP

CHAR(n)

n=>19

Result set
locator

SQL TYPE IS
RESULT_SET_LOCATOR

Use this data type only


for receiving result sets

Handling SQL errors


DCL DSNTIAR ENTRY OPTIONS (ASM,INTER,RETCODE);
CALL DSNTIAR ( sqlca, message, lrecl );
DCL DATA_LEN FIXED BIN(31) INIT(132);
DCL DATA_DIM FIXED BIN(31) INIT(10);
DCL 1 ERROR_MESSAGE ,
3 ERROR_LEN
FIXED BIN(15) UNAL INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);
.
.
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
Program Preparation
The program preparation JCL has the following steps:-

PL/I

23/01/2006

Page:- 105 / 119

The PL/I compiler is invoked to process only PL/I macros in the source. The compiler is invoked
with the options PARM='MACRO,NOSYNTAX,MDECK,NOINSOURCE'. The output source with
macros expanded is written to SYSPUNCH
DB/2 Pre compiler DSNHPC is invoked with PARM='HOST(PLI),SOURCE'. The DBRM is written
to the PDS member pointed to by DBRMLIB. The pre compiled source is written to SYSCIN.
The PL/I compiler is invoked and the Input source is presented via SYSIN (points to the same
data set that was created via SYSCIN in the previous step). The object code is written to
SYSLIN. DCLGEN and other copybooks must be input via SYSLIB.
LKED step. Primary Input via SYSLIN. Secondary input (Linkage editor control cards / or
subroutine object code) via SYSIN and SYSLIB respectively. The Load module is written to
SYSLMOD.
The stubs that need to be link edited depend on the environment in which the program is to run.
1. For the batch environment the stub DSNELI has to be link edited for resolving DB2 calls.
2. If you are creating a stored procedure link edit the DSNALI stub instead. If you are creating a
stored procedure SYSLMOD must point to a load library in the STEPLIB concatenation of the
<subsystem name>SPAS started procedure.
3. If you are creating a PL/I DB2 CICS program link edit the DSNCLI stub.
After preparing the program load module you have to bind the DBRM into a plan for a DB2
program that is not a stored procedure. For a stored procedure you must bind the DBRM into a
package.
Look at sample JCL set up on the system.
PLIDB2CL
for preparing a DB2 program that is not a stored procedure
PLIDB2SP
for preparing a DB2 program that is a stored procedure
DB2BNDPK
for binding a package for a stored procedure
DB2BNDPL
for binding a plan for a DB2 program that is not a stored procedure
DB2PLIR
Can be used for running the PL/I DB2 program (that may also invoke a
Stored Procedure)
Note that the only difference between PLIDB2CL and PLIDB2SP is the stub that gets link edited
with the program (DSNELI and DSNALI). Note also the PROC that is used is DSNHLI.
For a CICS DB2 program the JCL has the following steps:Invoke the PL/I compiler with PARM='MACRO,NOSYNTAX,MDECK,NOINSOURCE' to expand
PL/I macros if present.
Invoke the DB2 translator DSNHPC,PARM='HOST(PLI),SOURCE'
Invoke the CICS translator DFHECP1$ to translate EXEC CICS commands
Invoke the PL/I compiler for compiling the PL/I source
Link edit the program including the stub DSNCLI
Note:-If the program makes MQ calls also include the stub CSQCSTUB
Samples
General Notes on SPUFI:Set up SPUFI if you have not done so.
You need to allocate a data set for SPUFI output. The following JCL may be used:-

PL/I

23/01/2006

Page:- 106 / 119

//USERID1 JOB NOTIFY=&SYSUID,CLASS=A,MSGLEVEL=(1,1)


//PLIPROC JCLLIB ORDER=(USERID.PLI.PROCLIB)
//DOPROC EXEC PGM=IEFBR14
//NEW DD DSN=USERID.PLI.DB2OUT,DISP=(NEW,CATLG),
// SPACE=(TRK,(5,5)),DCB=(DSORG=PS,LRECL=4092,BLKSIZE=4096,RECFM=VB)
//
Allocate a PDS for input to SPUFI
Set up SPUFI to point to these data sets (input and output data sets in SPUFI panel).
Make sure that SPUFI defaults (using option D) are set to the correct DB2 subsystem name and
language is set to PLI.
DDL
All the following samples use the TESTPLI table using the following DDL:DROP TABLE USERID.TESTPLI;
COMMIT;
CREATE TABLE USERID.TESTPLI
(
CUSTNO
CHAR(5) NOT NULL,
CUSTDTLS
CHAR(75),
PRIMARY KEY(CUSTNO)
) IN KAMDB.USERIDS;
CREATE UNIQUE INDEX USERID.TESTPLINDX ON USERID.TESTPLI
(CUSTNO ASC);
Another member STORPROC has the DDL necessary to define a sample stored procedure that
will be described later in this material.
Sample-1.
This sample loads the above table with data read via ddname INFILE.
Error handling is via a call to DSNTIAR and this is the standard error handling we will follow for all
the following samples.

PLIDB21: PROC OPTIONS(MAIN);


DCL DATA_LEN FIXED BIN(31) INIT(132);
DCL DATA_DIM FIXED BIN(31) INIT(10);
DCL DSNTIAR EXTERNAL ENTRY OPTIONS(ASM,INTER,RETCODE);
DCL 1 ERROR_MESSAGE AUTOMATIC,
3 ERROR_LEN
FIXED BIN(15) UNAL
INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);
DCL INFILE FILE STREAM INPUT;
DCL (ADDR,NULL) BUILTIN;
OPEN FILE(SYSPRINT) LINESIZE(132);
EXEC SQL
INCLUDE SQLCA;
EXEC SQL
INCLUDE TESTPLI;
DCL EOF BIT(1) INIT('0'B);
ON ENDFILE(INFILE) EOF='1'B;
DCL LINE CHAR(80) BASED(PTR);

PL/I

23/01/2006

Page:- 107 / 119

PTR=ADDR(DCLTESTPLI);
GET FILE(INFILE) EDIT (LINE)(A(80));
DO WHILE (EOF);
EXEC SQL
INSERT INTO TESTPLI (CUSTNO,CUSTDTLS) VALUES
(:CUSTNO,:CUSTDTLS);
PUT SKIP EDIT('SQLCODE:-',SQLCODE)(A,A);
IF SQLCODE =0 THEN DO;
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
DO I=1 TO DATA_DIM;
PUT SKIP EDIT(ERROR_TEXT(I))(A);
END;
END;
GET FILE(INFILE) EDIT(LINE) (A(80));
END;
END PLIDB21;
Sample-2
This sample opens a cursor and reads all the rows in the TESTPLI table and prints them on
SYSPRINT.

PLIDB22: PROC OPTIONS(MAIN);


DCL DATA_LEN FIXED BIN(31) INIT(132);
DCL DATA_DIM FIXED BIN(31) INIT(10);
DCL DSNTIAR EXTERNAL ENTRY OPTIONS(ASM,INTER,RETCODE);
DCL 1 ERROR_MESSAGE AUTOMATIC,
3 ERROR_LEN
FIXED BIN(15) UNAL
INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);
DCL (ADDR,NULL) BUILTIN;
EXEC SQL
INCLUDE TESTPLI;
EXEC SQL DECLARE
INCURSOR CURSOR FOR
SELECT
CUSTNO,
CUSTDTLS
FROM TESTPLI;
EXEC SQL
INCLUDE SQLCA;
OPEN FILE(SYSPRINT) LINESIZE(132);
EXEC SQL
OPEN INCURSOR;
IF SQLCODE =0 THEN DO; CALL ERR_ROUTINE; STOP; END;
DO WHILE (SQLCODE=0);
EXEC SQL
FETCH INCURSOR INTO :CUSTNO,:CUSTDTLS;
IF SQLCODE =0 THEN
CALL ERR_ROUTINE;
ELSE
PUT SKIP EDIT(CUSTNO,CUSTDTLS)(A);
END;

PL/I

23/01/2006

Page:- 108 / 119

EXEC SQL
CLOSE INCURSOR;
ERR_ROUTINE: PROC;
PUT SKIP EDIT('SQLCODE:-',SQLCODE)(A,A);
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
DO I=1 TO DATA_DIM;
PUT SKIP EDIT(ERROR_TEXT(I))(A);
END;
END ERR_ROUTINE;
END PLIDB22;
Sample-3
This sample illustrates how we can update the table using a cursor.

PLIDB23: PROC OPTIONS(MAIN);


DCL DATA_LEN FIXED BIN(31) INIT(132);
DCL DATA_DIM FIXED BIN(31) INIT(10);
DCL DSNTIAR EXTERNAL ENTRY OPTIONS(ASM,INTER,RETCODE);
DCL 1 ERROR_MESSAGE AUTOMATIC,
3 ERROR_LEN
FIXED BIN(15) UNAL
INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);
DCL (ADDR,NULL) BUILTIN;
DCL UPDTFILE FILE RECORD INPUT SEQUENTIAL ENV(CONSECUTIVE);
DCL EOF BIT(1) INIT('0'B);
ON ENDFILE(UPDTFILE) EOF='1'B;
ON UNDEFINEDFILE(UPDTFILE) BEGIN;
PUT SKIP LIST('UPDTFILE OPEN FAILURE');
STOP;
END;
DCL 01 BUFFER,
02 CUSTNOI CHAR(5),
02 CUSTDTLSI CHAR(75);
EXEC SQL
INCLUDE TESTPLI;
EXEC SQL DECLARE
INCURSOR CURSOR FOR
SELECT
CUSTNO,
CUSTDTLS
FROM TESTPLI WHERE CUSTNO=:CUSTNOI
FOR UPDATE OF CUSTDTLS;
EXEC SQL
INCLUDE SQLCA;
OPEN FILE(SYSPRINT) LINESIZE(132);
OPEN FILE(UPDTFILE);
READ FILE(UPDTFILE) INTO(BUFFER);
DO WHILE (EOF);
EXEC SQL
OPEN INCURSOR;
IF SQLCODE =0 THEN DO; CALL ERR_ROUTINE; STOP; END;
EXEC SQL
FETCH INCURSOR INTO :CUSTNO,:CUSTDTLS;

PL/I

23/01/2006

Page:- 109 / 119

IF SQLCODE =0 THEN
CALL ERR_ROUTINE;
ELSE
DO;
PUT SKIP EDIT('EXISTING DATA:- ',CUSTNO,CUSTDTLS)(A);
EXEC SQL
UPDATE TESTPLI SET
CUSTDTLS = :CUSTDTLSI
WHERE CURRENT OF INCURSOR;
IF (SQLCODE=0) THEN CALL ERR_ROUTINE;
ELSE PUT SKIP EDIT
('SUCCESSFUL UPDATE OF KEY:-',CUSTNOI)(A);
END;
EXEC SQL
CLOSE INCURSOR;
READ FILE(UPDTFILE) INTO(BUFFER);
END;
PUT SKIP EDIT('EOJ')(A);
ERR_ROUTINE: PROC;
DCL PLIRETV BUILTIN;
PUT SKIP EDIT('SQLCODE:-',SQLCODE)(A,A);
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
PUT SKIP LIST(PLIRETV);
DO I=1 TO DATA_DIM;
PUT SKIP EDIT(ERROR_TEXT(I))(A);
END;
END ERR_ROUTINE;
END PLIDB23;
SQLDA
The SQLDA is an acronym for the SQL Descriptor area. As the name implies it is used by DB2 to
describe DB2 objects to an application. We start with the SQLDA structure itself in the context of
the PL/I host language to see what it contains:-

DECLARE
1 SQLDA BASED(SQLDAPTR),
2 SQLDAID CHAR(8),
2 SQLDABC FIXED(31) BINARY,
2 SQLN
FIXED(15) BINARY,
2 SQLD
FIXED(15) BINARY,
2 SQLVAR(SQLSIZE REFER(SQLN)),
3 SQLTYPE FIXED(15) BINARY,
3 SQLLEN
FIXED(15) BINARY,
3 SQLDATA POINTER,
3 SQLIND
POINTER,
3 SQLNAME CHAR(30) VAR;
Notes:The actual size of the structure is dependent on SQLN. This is because the number of elements
in the SQLVAR array is SQLN. (The concept is the same as in the COBOL clause OCCURS
DEPENDING ON .

PL/I

23/01/2006

Page:- 110 / 119

If SQLN is set to 750 (the maximum DB2 may need) the size is 16+750*44, which is 33016. The
44 is arrived at based on the size of each SQLVAR element. We say that the maximum number of
elements DB2 may need is 750 because if the SQLDA is used to describe the number of columns
in a row that maximum is 750. In actual practice we set SQLN to a realistic value, say 40, or to
another specific value if we are sure of it.
The structure if inserted into your PL/I application is a based structure. This means we have to
allocate storage for this before it is used at run time. Say we use it in the statement
EXEC SQL
DESCRIBE TABLE :PARM INTO :SQLDA USING NAMES;
If we assume that no matter what table name the variable PARM holds at run time, the maximum
number of columns is 100, then the size of SQLDA would then be 4416. Here is how we would
allocate it:DCL SQLDA_STORAGE CHAR(4416) BASED(P);
ALLOCATE SQLDA_STORAGE SET(SQLDAPTR);
/* NOW SET SQLN */
SQLDA.SQLN=100;
/* SQLN=100 WOULD DO FINE IF WE HAD ONLY ONE SQLDA*/
For a DESCRIBE TABLE SQL statement the structure would have been populated like below:SQLDAID
is set to
SQLDA
SQLDABC
is set to
SQLN*44+16
SQLN
remains at
100
SQLD
is set to
number of SQLVAR entries populated(number of columns)
Each SQLVAR entry describes a column as below:SQLTYPE
is set to
The data type of the column and whether NULLS are
permitted. For example 496 is for long integer (FIXED BINARY
With NULLS not allowed and 497 is for a long integer with
NULLS allowed. 452/453 are for fixed length character strings.
Look at SQL reference for the complete list.
SQLLEN
is set to
The number of bytes of storage required for the column data.
SQLDATA
is set to
X'0000zzzz', where zzzz is the associated CCSID.
SQLIND
is not used
SQLNAME
is set to
The column name.
Note that the context in which the SQLDA is used determines what the SQLVAR entries and the
value of the SQLD data items mean.
Sample-4
This sample illustrates the use of the SQLDA to describe a TABLE. When you run this program be
sure to pass a table name via the PARM JCL field.

PLIDB24: PROC(PARM) OPTIONS(MAIN);


DCL PARM CHAR(100) VARYING;
DCL DATA_LEN FIXED BIN(31) INIT(132);
DCL DATA_DIM FIXED BIN(31) INIT(10);
DCL DSNTIAR EXTERNAL ENTRY OPTIONS(ASM,INTER,RETCODE);
DCL 1 ERROR_MESSAGE AUTOMATIC,
3 ERROR_LEN
FIXED BIN(15) UNAL
INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);

PL/I

23/01/2006

Page:- 111 / 119

DCL (ADDR,NULL) BUILTIN;


EXEC SQL
INCLUDE TESTPLI;
EXEC SQL
INCLUDE SQLDA;
EXEC SQL
INCLUDE SQLCA;
OPEN FILE(SYSPRINT) LINESIZE(132);
DCL SQLDA_BUFFER CHAR(32000) BASED(P);
ALLOCATE SQLDA_BUFFER;
SQLDAPTR=P;
SQLN=100;
EXEC SQL
DESCRIBE TABLE :PARM INTO :SQLDA USING NAMES;
IF SQLCODE =0 THEN
DO;
PUT SKIP EDIT('NON ZERO SQLCODE FROM DESCRIBE',SQLCODE)
(A,F(4));
CALL ERR_ROUTINE;
STOP;
END;
PUT SKIP EDIT('SQLD ',SQLD)(A,F(3));
PUT SKIP EDIT('SQLN ',SQLD)(A,F(3));
PUT SKIP EDIT('SQLDAID ',SQLDAID)(A,A);
PUT SKIP EDIT('SQLDABC ',SQLDABC)(A,F(5));
DO I=1 TO SQLD;
PUT SKIP EDIT('SQLTYPE ',SQLTYPE(I))(A,F(3));
PUT SKIP EDIT('SQLLEN ',SQLLEN(I))(A,F(3));
PUT SKIP EDIT('SQLNAME ',SQLNAME(I))(A,A);
END;
PUT SKIP EDIT('EOJ')(A);
ERR_ROUTINE: PROC;
PUT SKIP EDIT('SQLCODE:-',SQLCODE)(A,A);
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
DO I=1 TO DATA_DIM;
PUT SKIP EDIT(ERROR_TEXT(I))(A);
END;
END ERR_ROUTINE;
END PLIDB24;
Sample-5
Having understood the use of the SQLDA, we present a series of samples where we ultimately
lead up to a sample where we use dynamic SQL and we dont know until run time either the table
name or the number and type of columns in each row.
In the first example of the series, we obtain from a CURSOR, by using the DESCRIBE CURSOR
SQL statement, the number of columns, the data type of each and the storage required for each
column data. The DESCRIBE CURSOR statement populates the SQLDA with this information.

PLIDB25: PROC(PARM) OPTIONS(MAIN);


DCL PARM CHAR(100) VARYING;
DCL DATA_LEN FIXED BIN(31) INIT(132);

PL/I

23/01/2006

Page:- 112 / 119

DCL DATA_DIM FIXED BIN(31) INIT(10);


DCL DSNTIAR EXTERNAL ENTRY OPTIONS(ASM,INTER,RETCODE);
DCL 1 ERROR_MESSAGE AUTOMATIC,
3 ERROR_LEN
FIXED BIN(15) UNAL
INIT((DATA_LEN*DATA_DIM)),
3 ERROR_TEXT(DATA_DIM) CHAR(DATA_LEN);
DCL (ADDR,NULL) BUILTIN;
DCL SQLDA_BUFFER CHAR(32000) BASED(P);
DCL DATALEN FIXED BINARY(15);
DCL DATA CHAR(DATALEN) CONTROLLED;
DCL INDICATOR FIXED BINARY(15) BASED(P3);
EXEC SQL
INCLUDE TESTPLI;
EXEC SQL DECLARE
INCURSOR CURSOR FOR
SELECT
CUSTNO,
CUSTDTLS
FROM TESTPLI;
EXEC SQL
INCLUDE SQLCA;
EXEC SQL
INCLUDE SQLDA;
OPEN FILE(SYSPRINT) LINESIZE(132);
/* SET UP THE SQLDA HERE FOR THE FETCH STATEMENT */
ALLOCATE SQLDA_BUFFER;
SQLDAPTR=P;
SQLN=100;
EXEC SQL
DESCRIBE TABLE :PARM INTO :SQLDA USING NAMES;
IF SQLCODE =0 THEN
DO;
PUT SKIP EDIT('NON ZERO SQLCODE FROM DESCRIBE',SQLCODE)
(A,F(4));
CALL ERR_ROUTINE;
STOP;
END;
/* ALLOCATE STORAGE FOR THE DATA AND INDICATOR VARAIABLES */
DO I=1 TO SQLD;
DATALEN=SQLLEN(I);
ALLOCATE DATA;
SQLDATA(I)=ADDR(DATA);
ALLOCATE INDICATOR SET(SQLIND(I));
END;
/* STORAGE ALLOCATION DONE */
/* SETUP OF SQLDA ENDS HERE */
EXEC SQL
OPEN INCURSOR;
IF SQLCODE =0 THEN DO; CALL ERR_ROUTINE; STOP; END;
DO WHILE (SQLCODE=0);
EXEC SQL
FETCH INCURSOR USING DESCRIPTOR :SQLDA;

PL/I

23/01/2006

Page:- 113 / 119

IF SQLCODE =0 THEN
CALL ERR_ROUTINE;
ELSE
CALL PRINT_ROW;
END;
ERR_ROUTINE: PROC;
PUT SKIP EDIT('SQLCODE:-',SQLCODE)(A,A);
CALL DSNTIAR ( SQLCA, ERROR_MESSAGE, DATA_LEN );
DO I=1 TO DATA_DIM;
PUT SKIP EDIT(ERROR_TEXT(I))(A);
END;
END ERR_ROUTINE;
/* PRINT EACH COLUMN */
PRINT_ROW: PROC;
DCL I FIXED BINARY(15);
DCL PDATA CHAR(1000) BASED(Q);
DCL VDATA CHAR(1000) VARYING;
DCL 01 VDATA_OVL BASED(P),
02 VARL FIXED BINARY(15),
02 VARD CHAR(1000);
P=ADDR(VDATA);
DO I=1 TO SQLD;
Q=SQLDATA(I);
VARD=SUBSTR(PDATA,1,SQLLEN(I));
VARL=SQLLEN(I);
PUT SKIP EDIT('COLNAME ',SQLNAME(I),' ',VDATA)(A(8),A(12),A,A);
END;
END PRINT_ROW;
/* END OF PRINT ROUTINE */
END PLIDB25;
Sample-6
PLIDB26 is a more refined version of this program and formats the output in a better way.
Look in the system for the source code for this program and understand it on your own.
Sample-7
PLIDB27:-This sample illustrates the use of dynamic SQL. We read the SQL statement from
SYSIN so we dont know until run time what SQL statement we are going to execute.
Here is the sequence of what we do in our program:-

EXEC SQL DECLARE S1 STATEMENT;


EXEC SQL DECLARE C1 CURSOR FOR S1;
/*in case we read in a SELECT stmt*/
Read the input from SYSIN into the variable STMT. Note that STMT
must be a VARYING character string.
EXEC SQL PREPARE S1 FROM :STMT;
EXEC SQL DESCRIBE S1 INTO :SQLDA;

PL/I

23/01/2006

Page:- 114 / 119

IF SQLD=0 THEN DO; /* non select statement */


EXEC SQL EXECUTE IMMEDIATE :STMT;
ELSE;
DO;
Initialize the SQLDA to allocate storage for the FETCH to
follow. Note the SQLD is set to the number of items of data
(column data) that the call will return.
For each instance of SQLVAR set:SQLDATA to dynamically acquired storage equal to SQLLEN in
SIZE.
SQLIND to dynamically acquired storage of 2 bytes.
EXEC SQL OPEN C1;
DO WHILE(SQLCODE=0);
EXEC SQL FETCH C1 USING DESCRIPTOR :SQLDA;
IF SQLCODE=0 THEN CALL PRINT_FUNCTION;
END;
END;
Look at the source code in PLIDB27. This program reads one SQL statement from SYSIN, that
can span multiple SYSIN records and terminated by a ;.
Sample-8
PLIDB28:-The next sample PLIDB28 is an extension of PLIDB27 and can process multiple SQL
statements from SYSIN one after the other until EOF. Each SQL statement can span up to at
most 8 lines (records) on SYSIN and must be terminated by a semi colon. The DB2 logic remains
the same.
Here is a sample of SYSIN statements that can be processed. The statements create a table, an
index over the key field, insert a row into the table and select all rows for printing://SYSIN DD *
CREATE TABLE KAM0001.TESTPLI4
(
FLD1
CHAR(5) NOT NULL,
FLD2
CHAR(5),
FLD3
CHAR(5),
FLD4
CHAR(5),
FLD5
CHAR(5),
FLD6
CHAR(5),
PRIMARY KEY(FLD1)
) IN KAMDB.KAM0001S;
CREATE UNIQUE INDEX KAM0001.TESTPLI4NDX ON KAM0001.TESTPLI4
(FLD1 ASC);
COMMIT;
INSERT INTO KAM0001.TESTPLI4
( FLD1,FLD2,FLD3,FLD4,FLD5,FLD6)
VALUES
('00100','FLD21','FLD31','FLD41','FLD51','FLD61');
COMMIT;
SELECT * FROM TESTPLI4;
/*

PL/I

23/01/2006

Page:- 115 / 119

Stored Procedures
A stored procedure is a compiled program, stored at a DB2 local or remote server, that can execute
SQL statements.
A client application program uses the SQL statement CALL to invoke the stored procedure.
Stored procedures can issue many SQL statements. The client program invokes the procedure using
a single SQL CALL statement to the DB2 Server. This reduces network traffic to a single send and
receive operation.
The client program needs authorization to execute the stored procedure. Authorizations to access the
server data directly is not needed. This eliminates the chances of fraudulent manipulation of server
data from the client system.
The client program is insulated from data base design changes as these have an impact only on the
stored procedure. So long as input and output parameters to the procedure dont change, the client
program does not need change.
Core business logic used by many clients can be implemented in a stored procedure.
An open systems client can access VSAM or IMS data through a stored procedure. The stored
procedure fetches VSAM or IMS data and populates a DB2 table. A cursor can be opened on the
table and the result set returned to the client. The client thus deals only with DB2 data. The stored
procedure can source this data from non DB2 sources.
For example to call procedure A on the local system:-

EXEC SQL CALL A(:EMP, . . .);


Calling with NULL values

EXEC SQL CALL A (:EMP :IEMP,


:PRJ :IPRJ,
:ACT :IACT,
:EMT :IEMT,
:EMS :IEMS,
:EME :IEME,
:TYPE :ITYPE,
:CODE :ICODE);
You might pass integer or character string constants or the null value to the stored procedure, as
in this example:
EXEC SQL CALL A ('IF1000', 90, 1.0, NULL, -01',:TYPE );
You might use a host variable for the name of the stored procedure:
EXEC SQL CALL :procnm (:EMP, . . .);
If you prefer to pass your parameters in a single structure, rather than as separate host variables,
you use this form:
EXEC SQL

CALL A USING DESCRIPTOR :sqlda

PL/I

23/01/2006

Page:- 116 / 119

Before the CALL statement is processed, the user must set the following fields in the SQLDA:
1. SQLN to indicate the number of SQLVAR occurrences provided in the SQLDA. This number
must not be less than SQLD.
2. SQLDABC to indicate the number of bytes of storage allocated for the SQLDA. This number
must be not be less than SQLN*44+16.
3. SQLD to indicate the number of variables used in the SQLDA when processing the
statement. This number must be the same as the number of parameters of the procedure.
4. SQLVAR occurrences to indicate the attributes of the variables.
Only a single instance (scalar value) of a parameter can be passed. You cannot pass structures
or arrays.
If a stored procedure abnormally terminates:
1. The calling program receives an SQL error as notification that the stored procedure failed.
2. DB2 places the calling programs unit of work in a must-rollback state.
For the Application Programmer
1. Write the Stored Procedure using the appropriate Language.
2. Pre-compile, compile and Link edit the stored procedure into the user library in the STEPLIB
concatenation of the stored procedure Address space(s) started proc.
3. Define the procedure to DB2. If you are at DB2 version 5 or below you must make an entry in
the DB2 Catalog table SYSIBM.SYSPROCEDURES. For later versions of DB2 you define the
procedure using the CREATE PROCEDURE DDL.
4. Bind the DBRM created in the compilation step into a Package. The collection in which this
package is bound is made known to DB2 when you defined the procedure in the step above.
Note that the following sample has been tested elsewhere but to run on your target
system, the DB2 subsystem must have been set up properly. It is therefore provided only
for illustrative purposes.
For the sample stored procedure illustrated below, the following DDL was used to define the
procedures, except for some changes in input parameters:-

DROP PROCEDURE KAM03SP;


COMMIT;
CREATE PROCEDURE KAM03SP
(V1 CHAR(5) IN, V2 INTEGER OUT,V3 CHAR(80) OUT)
LANGUAGE PLI
DETERMINISTIC
MODIFIES SQL DATA
EXTERNAL NAME KAM03SP
COLLID KAMCOL1
ASUTIME LIMIT 900
PARAMETER STYLE GENERAL

PL/I

23/01/2006

Page:- 117 / 119

STAY RESIDENT NO
RUN OPTIONS 'MSGFILE(OUTFILE),RPTSTG(ON),RPTOPTS(ON)'
NO WLM ENVIRONMENT
PROGRAM TYPE MAIN
SECURITY DB2
DYNAMIC RESULT SETS 10
COMMIT ON RETURN NO;
Notes on the DDL above that creates the stored procedure definition in the DB2 catalog:1.

The procedure name as known to DB2 is back lighted in yellow.

2.

The procedure name (load module) as known to MVS is back lighted in blue.

3.

The arguments passed are defined thus:(V1 CHAR(5) IN, V2 INTEGER OUT,V3 CHAR(80) OUT)
V1 is an input only argument CHAR(5)
V2 is an output only argument FIXED BINARY(31)
V3 is an output argument CHAR(80).
The stored procedure accesses the arguments like this:-

KAM03SP: PROC(P1,P2,P3) OPTIONS(MAIN);


DCL P1 CHAR(5);
DCL P2 FIXED BINARY(31);
DCL P3 CHAR(80);
4.

The procedure can modify SQL data.

5.

The DBRM from compiling the source is bound into a collection KAMCOL1.

6.

You must in addition to bind (or packadm) authority have execute authority on this
collection.

7.

A limit for time execution has been set via the ASUTIME parameter.

8.

The parameter passing style is of type general.(look at DB2 programming guide for other,
more complex argument passing styles).

9.

The program is not to stay resident and will be loaded afresh from the LOADLIB every time
needed.

10.

The LE run options are specified via RUN OPTIONS.

11.

The stored procedure does not run in an WLM controlled address space and will run in
<sub system name>SPAS (like DSN1SPAS).

12.

The stored procedure will run as a main program and you must therefore code
OPTIONS(MAIN) in the PROCEDURE statement.

13.

The stored procedure can at most return 10 result sets.

14.

The commit point is taken in caller code, not when the stored procedure returns.

PL/I

23/01/2006

Page:- 118 / 119

Sample Stored Procedure-KAM03SP


This sample inserts into the table TESTPLI via a call to a stored procedure one row whose values
are hard coded in the program. The objective is to keep the calling program and the stored
procedure as simple as possible, just to illustrate the concepts.
The Stored Procedure

*PROCESS NOT('~');
KAM03SP: PROC(P1,P2,P3) OPTIONS(MAIN);
DCL P1 CHAR(5);
DCL P2 FIXED BINARY(31);
DCL P3 CHAR(80);
EXEC SQL
INCLUDE SQLCA;
EXEC SQL
INCLUDE TESTPLI;
EXEC SQL
DECLARE INCURSOR CURSOR WITH RETURN FOR
SELECT CUSTNO,CUSTDTLS FROM TESTPLI
WHERE CUSTNO > :CUSTNO;
CUSTNO=P1;
EXEC SQL
OPEN INCURSOR;
IF SQLCODE ~=0 THEN DO;
P3='OPEN FAILED';
P2=SQLCODE;
RETURN;
END;
Calling Program:-KAM03SPM

*PROCESS NOT('~');
KAM3SPM: PROC OPTIONS(MAIN);
DCL ADDR BUILTIN;
DCL A1 CHAR(5) INIT('00050');
DCL A2 FIXED BINARY(31);
DCL A3 CHAR(80);
DCL CUSTNO CHAR(5);
DCL CUSTDTLS CHAR(75);
EXEC SQL
INCLUDE SQLCA;
DCL LCV SQL TYPE IS RESULT_SET_LOCATOR VARYING;
EXEC SQL
CALL KAM03SP (:A1,:A2,:A3);
IF SQLCODE~=466 THEN DO;
PUT SKIP EDIT('CALL SQLCODE:-',SQLCODE)(A,A);
PUT SKIP EDIT('FETCH SQLCODE:-',A2)(A,A);
PUT SKIP EDIT('FETCH MESSAGE:-',A3)(A,A);
RETURN;
END;
EXEC SQL
ASSOCIATE LOCATORS(:LCV) WITH PROCEDURE KAM03SP ;
IF SQLCODE~=0 THEN DO;
PUT SKIP EDIT('ASSOCIATE FAILED',SQLCODE)(A,F(4));

PL/I

23/01/2006

Page:- 119 / 119

RETURN;
END;
EXEC SQL
ALLOCATE CSR CURSOR FOR RESULT SET :LCV;
IF SQLCODE~=0 THEN DO;
PUT SKIP EDIT('ALLOCATE FAILED',SQLCODE)(A,F(4));
RETURN;
END;
SQLCODE=0;
DO WHILE (SQLCODE=0);
EXEC SQL
FETCH CSR INTO :CUSTNO, :CUSTDTLS;
PUT SKIP EDIT(CUSTNO,CUSTDTLS)(A);
END;
PUT SKIP EDIT('FETCH LAST SQLCODE',SQLCODE)(A,F(4));
END KAM3SPM;

You might also like