Reference
A single-page cheat sheet covering the patterns you’ll reach for most often. Not exhaustive — the IBM i RPG Reference is the full spec. This page is what to keep open in a second tab while you work.
Jump to:
- Free-format RPGLE
- Common BIFs
- Embedded SQL essentials
- SQLSTATEs worth knowing
- MONITOR error handling
- Daily-use CL commands
- Compile commands
Free-format RPGLE
Source file header
**free
ctl-opt dftactgrp(*no) actgrp(*caller)
option(*nodebugio:*srcstmt)
main(Main);
**free— must be on line 1, column 1dftactgrp(*no)— required for modern procedure-based programsactgrp(*caller)— run in the caller’s activation groupoption(*nodebugio:*srcstmt)— better debugger line-mappingmain(Main)— declare an explicit main procedure
Data types
| Type | Syntax | Notes |
|---|---|---|
| Fixed-length char | char(n) |
Pads with spaces |
| Variable-length char | varchar(n) |
No padding, up to 16 MB |
| Packed decimal | packed(n:d) |
n digits total, d decimals. Most common numeric |
| Zoned decimal | zoned(n:d) |
Legacy |
| Integer | int(3), int(5), int(10), int(20) |
1, 2, 4, 8 bytes |
| Date | date |
Stored internally, compiler-enforced arithmetic |
| Time | time |
HH:MM:SS |
| Timestamp | timestamp |
Date + time + microseconds |
| Boolean | ind |
Values *on / *off |
| Pointer | pointer |
Raw pointer, rarely needed |
Variable declarations
dcl-s simpleVar packed(9:2);
dcl-s initedVar int(10) inz(0);
dcl-s sizedArray char(10) dim(50);
dcl-s constVar packed(5:2) const inz(3.14);
Data structures
// Always use qualified
dcl-ds item qualified;
code char(15);
name varchar(50);
price packed(9:2);
end-ds;
// Template — no memory allocated, just a shape
dcl-ds itemTemplate qualified template;
code char(15);
name varchar(50);
end-ds;
// Declare another DS matching the template
dcl-ds anotherItem likeds(itemTemplate);
// DS matching a database table's schema
dcl-ds prodRec extname('PRODUCT') qualified end-ds;
// DS matching a record format
dcl-ds supRec likerec(SP_REC);
// Array of DSes
dcl-ds orderLines qualified dim(100);
product char(15);
quantity packed(7:0);
end-ds;
Control flow
// If / elseif / else
if condition;
...
elseif otherCondition;
...
else;
...
endif;
// Select / when
select;
when x = 1;
...
when x = 2;
...
other;
...
endsl;
// For loop
for i = 1 to 10; // ascending
...
endfor;
for i = 10 downto 1; // descending
...
endfor;
for i = 1 to 100 by 5; // step
...
endfor;
// Do-while (condition checked at top)
dow condition;
...
if earlyExit; leave; endif; // break
if skipRest; iter; endif; // continue
enddo;
// Do-until (condition checked at bottom)
dou condition;
...
enddo;
// FOR-EACH (iterate array or DS array)
for-each element in myArray;
...
endfor;
Procedures
// Prototype — declare at top for forward reference
dcl-pr CalcPrice packed(11:2);
qty packed(7:0) const;
price packed(9:2) const;
end-pr;
// Definition — the body
dcl-proc CalcPrice;
dcl-pi *n packed(11:2);
qty packed(7:0) const;
price packed(9:2) const;
end-pi;
return qty * price;
end-proc;
// Exported from service program
dcl-proc GetPrice export;
...
end-proc;
// Parameter keywords
const // read-only, can't be modified
value // pass by value (copy)
options(*nopass) // optional parameter, check with %parms()
options(*omit) // allow *OMIT placeholder
options(*varsize) // accept variable-size strings
Indicators
dcl-s isActive ind;
isActive = *on;
if isActive;
...
endif;
// Toggle
isActive = not isActive;
// Assign from comparison
isActive = (status = 'A');
Common BIFs
Grouped by purpose.
Strings
%trim(s) // trim leading and trailing spaces
%trimr(s) // trim trailing (right) only
%triml(s) // trim leading (left) only
%len(s) // length of value (for varchar) or declared (for char)
%subst(s : pos : len) // substring; len optional (to end if omitted)
%scan(needle : haystack) // position of first match, 0 if not found
%scanrpl(old : new : s) // replace all occurrences
%upper(s) // convert to uppercase
%lower(s) // convert to lowercase
%replace(new : s : pos : len) // replace a section
%xlate(from : to : s) // character-by-character translate
Type conversion
%char(value) // anything to character
%char(date : *iso) // date formatted as ISO
%int(s) // char or numeric to integer
%float(s) // to floating-point
%dec(s : digits : decimals) // to packed decimal, with precision
%date(s : *iso) // string to date
%time(s : *iso) // string to time
%timestamp(s) // string to timestamp
Numeric
%abs(n) // absolute value
%rem(a : b) // remainder (modulo)
%div(a : b) // integer division (like / but returns int)
%int(n) // truncate to integer
%round(n : places) // round half-up to decimals
Dates
%date() // today
%time() // current time
%timestamp() // now
%days(n) // duration: n days
%weeks(n) // n weeks
%months(n) // n months
%years(n) // n years
%hours(n), %minutes(n), %seconds(n)
%diff(d1 : d2 : *days) // difference in days (d1 - d2)
%subdt(dateValue : *d) // extract day of month
%subdt(dateValue : *m) // extract month
%subdt(dateValue : *y) // extract year
File and SQL status
%found(FILENAME) // last CHAIN/SETLL found a match
%eof(FILENAME) // last READ hit end of file
%error // last operation raised an error
%status // numeric error status
// Inside a monitor block
%status // current error status code
%error // short error description text
Arrays
%elem(array) // number of elements
%lookup(needle : array) // returns position or 0
%lookup(needle : array : startIdx) // starting at specific position
%xfoot(numericArray) // sum all elements
Parameters and misc
%parms() // number of parameters passed to current procedure
%addr(var) // address (pointer) to a variable
Embedded SQL essentials
File extension
Use .sqlrpgle for any source with exec sql. Not .rpgle.
Standard SQL options (paste at top of every SQL program)
exec sql
set option commit = *none,
datfmt = *iso,
closqlcsr = *endactgrp;
Single-row query
exec sql
select PR_NAME, PR_PRICE
into :localName, :localPrice
from PRODUCT
where PR_PROD = :inputCode;
if sqlstate <> '00000';
// handle error
endif;
Cursor for multi-row query
// Declare (doesn't execute)
exec sql
declare prodCursor cursor for
select PR_PROD, PR_QOH
from PRODUCT
where PR_ACTIVE = 'Y'
order by PR_PROD;
// Open (executes the query)
exec sql open prodCursor;
// Fetch loop
exec sql fetch next from prodCursor into :prodVar, :qohVar;
dow sqlstate = '00000';
// process row
exec sql fetch next from prodCursor into :prodVar, :qohVar;
enddo;
// Close
exec sql close prodCursor;
Fetching into a data structure
dcl-ds prodRec extname('PRODUCT') qualified end-ds;
exec sql
declare prodCursor cursor for
select * from PRODUCT where PR_SUPL = :suplCode;
exec sql open prodCursor;
exec sql fetch next from prodCursor into :prodRec;
dow sqlstate = '00000';
// prodRec.PR_PROD, prodRec.PR_PRICE, etc.
exec sql fetch next from prodCursor into :prodRec;
enddo;
exec sql close prodCursor;
Insert / Update / Delete
exec sql
insert into PRODUCT
(PR_PROD, PR_SUPL, PR_PRICE, PR_ACTIVE, PR_CHGDT)
values
(:newCode, :newSupl, :newPrice, 'Y', current_date);
exec sql
update PRODUCT
set PR_PRICE = :newPrice,
PR_CHGDT = current_date
where PR_PROD = :prodCode;
exec sql
delete from REORDCND
where RC_CRTDT < current_date - 30 days;
// Check rows affected
if sqlerrd(3) = 0;
// no rows were affected — might be worth flagging
endif;
Transactions
exec sql commit;
exec sql rollback;
Dynamic SQL (when you need a table name as variable)
dcl-s sql varchar(500);
sql = 'select count(*) from ' + %trim(tableName);
exec sql prepare stmt from :sql;
exec sql execute stmt into :count;
SQLSTATEs worth knowing
The five-character code returned after every SQL statement. First two characters are the class.
Success and “expected” results
| State | Meaning |
|---|---|
00000 |
Success |
01xxx |
Warning (operation completed, but with caveats) |
02000 |
No row found (SELECT INTO) or no more rows (FETCH) |
Common errors
| State | Meaning |
|---|---|
21000 |
Result set has more than one row (for SELECT INTO) |
22001 |
String data right truncation (your target is too small) |
22003 |
Numeric value out of range |
22007 |
Invalid date/time format |
22012 |
Division by zero |
23502 |
NULL in a NOT NULL column |
23505 |
Unique constraint violation (duplicate key) |
23503 |
Foreign key violation |
24501 |
Cursor is not open |
24504 |
Cursor is in an invalid state (e.g., positioned after end) |
42601 |
Syntax error |
42703 |
Column doesn’t exist |
42704 |
Object doesn’t exist (table, etc.) |
42802 |
Wrong number of values for an INSERT |
42818 |
Operands of different types |
Pattern-matching class codes
When you want to branch on error category rather than exact code:
22xxx— data exception (type conversion, range, format)23xxx— integrity constraint violation42xxx— syntax or access violation57xxx— resource not available or operator intervention58xxx— system error
Branch with a range check:
if %subst(sqlstate : 1 : 2) = '23';
// any constraint violation
endif;
MONITOR error handling
Basic structure
monitor;
// risky code
on-error;
// handle any error
endmon;
Specific error codes
monitor;
result = numerator / denominator;
on-error *zero-divide;
result = 0;
on-error *out-of-range;
result = 9999999999.99;
on-error 00100 : 00199; // range
// catches status 00100 through 00199 inclusive
on-error;
// catch-all
endmon;
Useful named statuses
| Status | Meaning |
|---|---|
*zero-divide |
Division by zero (00102) |
*out-of-range |
Value out of range (00103) |
*string-error |
String operation failed (00100) |
*decimal-data |
Invalid decimal data (00907) |
Inspect the error
on-error;
dsply ('Error ' + %char(%status) + ': ' + %trim(%error));
endmon;
Key rule
monitor catches RPG runtime errors only. SQL errors set sqlstate instead. Check both:
monitor/on-error— for%dec, divisions, overflows, array bounds, pointerssqlstatecheck — for everyexec sqloperation
Daily-use CL commands
Library and library list
| Command | What it does |
|---|---|
WRKLIB |
Work with libraries |
CRTLIB |
Create library |
DLTLIB |
Delete library |
DSPLIBL |
Display library list |
ADDLIBLE |
Add library to library list |
RMVLIBLE |
Remove library from library list |
CHGLIBL |
Change library list (replace entire list) |
CHGCURLIB |
Change current library |
Objects
| Command | What it does |
|---|---|
WRKOBJ |
Work with objects |
DSPOBJD |
Display object description |
CRTDUPOBJ |
Duplicate an object |
MOVOBJ |
Move object to a different library |
DLTOBJ |
Delete object |
RNMOBJ |
Rename object |
CHGOBJOWN |
Change object owner |
Source physical files and members
| Command | What it does |
|---|---|
CRTSRCPF |
Create source physical file |
WRKMBRPDM |
Work with source members (PDM) |
ADDPFM |
Add physical file member |
RMVM |
Remove member |
CPYSRCF |
Copy source file / members |
Jobs
| Command | What it does |
|---|---|
WRKACTJOB |
Work with active jobs |
WRKSBMJOB |
Work with submitted jobs |
WRKJOB |
Work with current or specific job |
DSPJOB |
Display job details |
SBMJOB |
Submit job to batch |
HLDJOB / RLSJOB / ENDJOB |
Hold, release, end job |
WRKJOBSCDE |
Work with job schedule entries |
ADDJOBSCDE |
Add scheduled job |
Job log and messages
| Command | What it does |
|---|---|
DSPJOBLOG |
Display job log |
DSPMSG |
Display messages |
SNDPGMMSG |
Send program message |
SNDMSG |
Send message to user or queue |
WRKMSG |
Work with messages |
Spool files
| Command | What it does |
|---|---|
WRKSPLF |
Work with spool files |
DSPSPLF |
Display spool file |
CPYSPLF |
Copy spool file to a PF |
DLTSPLF |
Delete spool file |
Files and data
| Command | What it does |
|---|---|
DSPFD |
Display file description |
DSPFFD |
Display file field descriptions |
DSPPFM |
Display physical file member (view data) |
CLRPFM |
Clear physical file member (empty data) |
RGZPFM |
Reorganize PFM (reclaim deleted-record space) |
STRSQL |
Interactive SQL session |
RUNSQLSTM |
Run SQL statements from a source member |
Overrides
| Command | What it does |
|---|---|
OVRDBF |
Override database file |
OVRPRTF |
Override printer file |
OVRDSPF |
Override display file |
DSPOVR |
Display active overrides |
DLTOVR |
Delete override |
Authority and users
| Command | What it does |
|---|---|
DSPUSRPRF |
Display user profile |
WRKUSRPRF |
Work with user profiles |
DSPOBJAUT |
Display object authority |
GRTOBJAUT |
Grant object authority |
RVKOBJAUT |
Revoke object authority |
Service and diagnostics
| Command | What it does |
|---|---|
DSPSRVPGM |
Display service program info (including exports) |
DSPMOD |
Display module info |
DSPBNDDIR |
Display binding directory |
WRKBNDDIR |
Work with binding directories |
Compile commands
RPG
CRTBNDRPG PGM(YOURLIB/YOURPGM)
SRCFILE(YOURLIB/QRPGLESRC)
SRCMBR(YOURPGM)
DBGVIEW(*SOURCE)
OPTION(*EVENTF)
Compiles a bound RPG program. Use for plain .rpgle files with no embedded SQL.
SQLRPGLE
CRTSQLRPGI OBJ(YOURLIB/YOURPGM)
SRCFILE(YOURLIB/QRPGLESRC)
SRCMBR(YOURPGM)
OBJTYPE(*PGM)
COMMIT(*NONE)
DBGVIEW(*SOURCE)
OPTION(*EVENTF *XREF)
For .sqlrpgle — the SQL preprocessor runs first, then CRTBNDRPG internally.
For a module (service program input), change OBJTYPE(*PGM) to OBJTYPE(*MODULE).
CL
CRTBNDCL PGM(YOURLIB/YOURPGM)
SRCFILE(YOURLIB/QCLLESRC)
SRCMBR(YOURPGM)
DBGVIEW(*SOURCE)
Service program (two-step)
// Step 1: Compile as module
CRTSQLRPGI OBJ(YOURLIB/SVCMOD)
SRCFILE(YOURLIB/QRPGLESRC)
SRCMBR(SVCMOD)
OBJTYPE(*MODULE)
...
// Step 2: Create service program from module(s)
CRTSRVPGM SRVPGM(YOURLIB/SVCPGM)
MODULE(YOURLIB/SVCMOD)
EXPORT(*ALL)
ACTGRP(*CALLER)
Binding directories
// Create
CRTBNDDIR BNDDIR(YOURLIB/MYBND)
// Add a service program to it
ADDBNDDIRE BNDDIR(YOURLIB/MYBND)
OBJ((YOURLIB/SVCPGM *SRVPGM))
// Reference in ctl-opt of a consuming program:
// ctl-opt ... bnddir('MYBND');
Logical files from IFS source
CL doesn’t have a built-in CRTLF from IFS, but most shops use a utility (like CRTLFIFS from the Code for IBM i community). Check the Code for IBM i actions config for the command pattern.
A few final patterns
The “ok flag” output parameter
Return a value and a success indicator — so the caller can distinguish “succeeded with value 0” from “failed, ignore value”:
dcl-proc ParseAmount;
dcl-pi *n packed(9:2);
input char(20) const;
ok ind;
end-pi;
dcl-s result packed(9:2);
monitor;
result = %dec(%trim(input) : 9 : 2);
ok = *on;
on-error;
ok = *off;
result = 0;
endmon;
return result;
end-proc;
The read-then-loop pattern (native I/O)
setll *start FILE;
read FILE;
dow not %eof(FILE);
// process current record
read FILE;
enddo;
Priming read before the loop; advancing read at the bottom. Never inside the loop condition.
The cursor-fetch pattern (SQL)
exec sql declare c1 cursor for ...;
exec sql open c1;
exec sql fetch next from c1 into :vars;
dow sqlstate = '00000';
// process
exec sql fetch next from c1 into :vars;
enddo;
exec sql close c1;
Same shape as the native pattern. Internalize once, recognize everywhere.
Tiered conditionals — always check highest tier first
if qty >= 100;
discount = 0.10;
elseif qty >= 50;
discount = 0.05;
else;
discount = 0;
endif;
Checking qty >= 50 first would eat the >= 100 branch — a common bug. Always check highest threshold first in any tiered logic.
This page is a living reference. If something’s missing or unclear, open a PR or suggest edits via the link at the bottom.