A very real behavior in IBM i ILE RPG where activation groups and Open Data Paths (ODPs) affect how file reads behave across programs.
๐ฏ Scenario Summary
-
Two programs: PGM1 and PGM2
-
Both are compiled with the same activation group (e.g.,
'SALEGROUP'
) -
Both use the same file, say
SALESF
-
PGM1 does
SETLL
+READ
onSALESF
-
Then PGM2 is called and also does
READ
onSALESF
, without SETLL
๐ง What Happens
1. File sharing occurs via the same activation group
Because PGM1 and PGM2 share the same activation group, IBM i reuses the Open Data Path (ODP) for SALESF
. That means:
-
The file cursor/position is shared
-
The file stays open across program calls
2. PGM2 continues reading where PGM1 left off
So in your case:
-
PGM1
does:setll key SALESF; read SALESF; // reads record #1 read SALESF; // reads record #2
-
Then
PGM2
does:read SALESF; // continues from record #3
Because PGM2 never did SETLL
, it just continues reading from wherever PGM1 left off.
โ Behavior: This is normal and expected in shared activation groups
Shared Component | Behavior |
---|---|
File ODP | Shared |
File cursor | Shared (position retained) |
File state | Shared (open, locked, etc.) |
Buffer, vars | Not shared unless explicitly passed |
๐ Potential Pitfalls
Issue | Risk |
---|---|
Forgetting to SETLL in PGM2 |
Unexpected record or skipped data |
Assuming files are "fresh" per program | Leads to bugs in batch or API jobs |
Files left open | Can block others or cause locking issues |
โ Best Practices
Practice | Why |
---|---|
Always SETLL or CHAIN before READ |
Ensures correct positioning |
Use CLOSE if you want a clean state |
Frees ODP and file cursor |
Use ACTGRP(*NEW) if you want isolation |
Avoids shared file behavior |
Document shared file behavior | Helps in modular/multi-program flows |
๐ก Bonus Tip: Detecting ODP Reuse
You can trace ODP usage with a debug tool or use debug logging to monitor how many times a file is opened and where it starts reading.
Below is a working demo of two RPGLE programs (PGM1
and PGM2
) using the same activation group and sharing a physical file (SALESF
). The goal is to demonstrate how PGM2
reads from where PGM1
left off.
๐งพ Step 1: Physical File — SALESF
Create this DDS source:
A R SALESREC
A SALEID 5A
A AMOUNT 9P 2
A K SALEID
Compile it with:
CRTPF FILE(MYLIB/SALESF) SRCFILE(MYLIB/QDDSSRC)
And insert some sample data (via SQL or DFU):
INSERT INTO SALESF VALUES ('S0001', 100.00);
INSERT INTO SALESF VALUES ('S0002', 200.00);
INSERT INTO SALESF VALUES ('S0003', 300.00);
INSERT INTO SALESF VALUES ('S0004', 400.00);
๐งพ Step 2: Program PGM1 — READ
with SETLL
ctl-opt actgrp('SALEGROUP') dftactgrp(*no);
dcl-f SALESF keyed usage(*input);
dcl-s saleid char(5);
dcl-s amount packed(9:2);
setll 'S0001' SALESF;
read SALESF;
if not %eof(SALESF);
saleid = SALEID;
amount = AMOUNT;
dsply ('PGM1: ' + saleid + ' - ' + %char(amount));
endif;
read SALESF;
if not %eof(SALESF);
saleid = SALEID;
amount = AMOUNT;
dsply ('PGM1: ' + saleid + ' - ' + %char(amount));
endif;
// Now call PGM2 (reads same file without SETLL)
call 'PGM2';
๐งพ Step 3: Program PGM2 — READ
Without SETLL
ctl-opt actgrp('SALEGROUP') dftactgrp(*no);
dcl-f SALESF keyed usage(*input);
dcl-s saleid char(5);
dcl-s amount packed(9:2);
// No SETLL โ continues from where PGM1 left off
read SALESF;
if not %eof(SALESF);
saleid = SALEID;
amount = AMOUNT;
dsply ('PGM2: ' + saleid + ' - ' + %char(amount));
endif;
read SALESF;
if not %eof(SALESF);
saleid = SALEID;
amount = AMOUNT;
dsply ('PGM2: ' + saleid + ' - ' + %char(amount));
endif;
๐งช Expected Output
PGM1: S0001 - 100.00
PGM1: S0002 - 200.00
PGM2: S0003 - 300.00
PGM2: S0004 - 400.00
Notice how PGM2 picks up exactly where PGM1 left off, because the file ODP and cursor are shared via the same activation group.
โ
Optional: Isolate Behavior with *NEW
If you change actgrp('SALEGROUP')
to actgrp(*NEW)
in either program, the file will be reopened, and the cursor will reset — so PGM2
would start from the top again.