An Open Data Path (ODP) in IBM i is a system-managed structure that represents the path between a program and a database file once the file is opened. It controls how records are read, updated, and positioned — including:
-
File pointer position
-
Record format
-
Access path (keyed/sequential)
-
Locking and sharing info
๐ง Simple Definition:
The ODP is the "live connection" between your program and the file — it's what IBM i uses to track where you are in the file, how you opened it, and who else is using it.
โ Key Responsibilities of ODP
Function | Description |
---|---|
Track file position | Keeps current location for READ , SETLL , CHAIN , etc. |
Maintain access path | Manages index used for keyed access (K fields in DDS) |
File sharing | Shared across programs within the same activation group |
Manage open mode | Tracks if file is open for input, output, update |
๐งพ Example: ODP Behavior with READ
setll 'C100' CUSTOMERF;
read CUSTOMERF; // ODP now positioned at next record
read CUSTOMERF; // Continues from last position
Each READ
advances the ODP's cursor.
๐ Types of ODP
Type | Description |
---|---|
Shared ODP | Used by multiple programs in the same activation group |
Private ODP | Used by one program only (e.g., in *NEW actgrp or DFTACTGRP) |
Reused ODP | Reused across program calls if file and actgrp are the same |
๐งจ When ODP Causes Issues
Symptom | Root Cause |
---|---|
READ returns wrong record |
ODP already positioned by another program |
CHAIN fails after recompilation |
Format mismatch or shared stale ODP |
File already open error | File open in shared ODP with different params |
Level check errors | ODP referencing outdated format |
โ Best Practices
Recommendation | Reason |
---|---|
Always SETLL before READ |
Resets ODP position |
Use *NEW activation group when needed |
Forces new ODP |
Use CLOSE to reset ODP cleanly |
Especially in reusable jobs/APIs |
Avoid sharing files in long-running groups | Prevents ODP confusion across program calls |
๐ Inspecting ODPs (Indirectly)
While IBM i doesn't let you directly "see" ODPs, you can observe their behavior using:
-
Debug (
STRDBG
) → see file pointer changes -
Job log and DSPJOB → monitor open file references
-
DSPPGMREF / DSPDBR → trace usage relationships
Let’s walk through a clear example where two programs — using different activation groups — each get their own Open Data Path (ODP) to the same file, leading to different read behavior even though the file and logic are the same.
๐ฏ Scenario: Two Programs, Same File, Different Activation Groups
Files:
-
File:
SALESF
-
Has 4 records: S001, S002, S003, S004
-
๐งพ Program A (PGMA) — Uses *NEW
Activation Group
ctl-opt actgrp(*new) dftactgrp(*no);
dcl-f SALESF usage(*input) keyed;
read SALESF;
if not %eof(SALESF);
dsply ('PGMA: ' + SALEID);
endif;
๐งพ Program B (PGMB) — Also Uses *NEW
ctl-opt actgrp(*new) dftactgrp(*no);
dcl-f SALESF usage(*input) keyed;
read SALESF;
if not %eof(SALESF);
dsply ('PGMB: ' + SALEID);
endif;
โ What Happens:
Each program has:
-
Its own activation group
-
Its own ODP
-
So even though both programs read
SALESF
, they each start at the top
If you run:
CALL PGMA
CALL PGMB
Output:
PGMA: S001
PGMB: S001
โก Even though PGMA read record S001, PGMB also starts at S001 because it got a separate ODP when it opened the file.
๐ Now, Modify PGMB to Use Same Activation Group:
In PGMB:
ctl-opt actgrp('SALEGROUP');
And also recompile PGMA with the same activation group:
ctl-opt actgrp('SALEGROUP');
Now, run:
CALL PGMA
CALL PGMB
Output:
PGMA: S001
PGMB: S002
โก PGMB inherits the ODP from PGMA and continues from where PGMA left off!
๐ Takeaway: Activation Group Controls ODP Sharing
Activation Group | ODP Behavior |
---|---|
Same group | Shared ODP |
*NEW or *DFTACTGRP |
Separate ODP |
Mixed | Can cause conflicts or confusion |
๐ Debugging ODP Issues in Service Programs
If your service program is:
-
Using files without
CLOSE
-
Called from programs with different actgrps
Then:
-
ODPs can stay open
-
Reads may act unpredictably
-
Files may show
already open
orlevel check
errors
๐ง Solution:
-
Use
*CALLER
in service program:ctl-opt actgrp(*caller);
-
Or explicitly
CLOSE
files at end of procedure
Let’s build a full working example that shows:
-
A main program (
MAINPGM
) calls a service program (SRVPGM
) -
Both use a shared file (
SALESF
) -
Improper activation group handling causes ODP confusion or even file already open errors
-
Then we'll fix it using
*CALLER
andCLOSE
๐งพ Step 1: Physical File — SALESF
A R SALESREC
A SALEID 5A
A AMOUNT 9P 2
A K SALEID
Populate with:
INSERT INTO SALESF VALUES ('S001', 100.00);
INSERT INTO SALESF VALUES ('S002', 200.00);
๐งพ Step 2: Service Program Source — SRVREAD
ctl-opt actgrp('SALEGROUP') dftactgrp(*no); // โ bad for shared services
dcl-f SALESF usage(*input) keyed;
dcl-proc ReadNextSale export;
dcl-s sale char(5);
read SALESF;
if not %eof(SALESF);
sale = SALEID;
dsply ('SRVPGM: ' + sale);
else;
dsply 'SRVPGM: EOF';
endif;
// Optional fix: close file to reset ODP
// close SALESF;
end-proc;
๐งพ Step 3: Main Program — MAINPGM
ctl-opt actgrp('SALEGROUP') dftactgrp(*no);
dcl-pr ReadNextSale extproc(*dclcase);
end-pr;
dcl-f SALESF usage(*input) keyed;
read SALESF;
if not %eof(SALESF);
dsply ('MAINPGM: ' + SALEID);
endif;
// Now call service program
ReadNextSale();
๐งจ Problem: ODP Conflict
When you run MAINPGM
, it opens SALESF
and reads S001.
Then it calls ReadNextSale()
from SRVPGM
, which tries to open SALESF
again — but:
-
ODP is already in use
-
File may be locked or skip to S002
-
You may get:
RNX1216 โ File already open RNX1217 โ File not found or not loaded
-
Or unexpected read behavior (record skipping)
โ
Fix 1: Use *CALLER
in Service Program
Change SRVREAD
:
ctl-opt actgrp(*caller); // โ
Inherits activation group from MAINPGM
This allows the ODP to be shared safely — SALESF
is opened once by MAINPGM and reused by SRVPGM
.
โ
Fix 2: Use CLOSE
inside service proc
If you want to force a clean read each time ReadNextSale()
is called, add:
close SALESF;
But this is less preferred than managing activation groups properly.
๐ Lesson Summary
Mistake | Result |
---|---|
Service PG in own actgrp | File re-opened, ODP conflict |
Same file in both programs | READ may skip or error |
Using *CALLER |
Shares file, avoids conflicts |
Using *NEW in caller |
Resets everything clean |