I/O Operations in RPG
Learn input and output in RPG - console messages, interactive input, database file access, printer reports, and embedded SQL on IBM i
Input and output are where RPG shows its heritage most clearly. The language was born as the Report Program Generator - a tool for reading business records and producing reports - so file and record I/O are not add-on libraries but core parts of the language itself. Where a scripting language reaches for a print function and a file handle, RPG declares files with dcl-f, reads records with read and chain, and writes output records with write.
Because RPG is a procedural, statically-typed business language, its I/O model is built around records rather than raw byte streams. A physical file (a database table on IBM i) is described externally, and its fields become variables in your program automatically. This tight coupling between the program and the database is one of RPG’s defining strengths, and it is why embedded SQL feels so natural in the language.
In this tutorial you will learn how to display and format console output, read interactive input, read and search database files, generate a printer report (RPG’s namesake), and perform database I/O with embedded SQL. Since RPG runs exclusively on IBM i, there is no Docker image - the examples explain the code and show how to compile and run it on an IBM i system.
Note: RPG has no open-source compiler and no Docker image. The
docker_imagefront matter is set tonone. All examples must be compiled and run on an IBM i (formerly AS/400) system.
Console Output and Formatting
The dsply operation is the simplest form of output. Beyond plain text, RPG formats numbers with built-in functions (BIFs) like %char for simple conversion and %editc for business-style editing with commas and decimals.
Create a file named io_console.rpgle:
**FREE
dcl-s amount packed(11:2) inz(1234.56);
dcl-s qty int(10) inz(5);
dcl-s total packed(13:2);
dcl-s msg char(52);
// Plain text output
msg = 'Order Report';
dsply msg;
// Numeric to character with %CHAR
msg = 'Quantity: ' + %char(qty);
dsply msg;
// Formatted decimal with %EDITC (edit code 1 adds commas)
msg = 'Unit price: ' + %trim(%editc(amount:'1'));
dsply msg;
// Compute and display a total
total = qty * amount;
msg = 'Total: ' + %trim(%editc(total:'1'));
dsply msg;
*inlr = *on;
Key points:
dsplysends a message to the program message queue; on a 5250 terminal each message waits for Enter, and in batch it goes to the job log.%charconverts a numeric value to its plain character form (5,6172.80).%editcapplies an edit code - here'1'inserts thousands separators and a decimal point (1,234.56) while suppressing leading zeros.%trimremoves the blanks that zero-suppression leaves in front of an edited number so concatenation looks clean.
Interactive Input
The dsply operation can also read a value. When you supply a response field as its third operand, it prompts the user and returns what they type. The character input is then converted to the type you need with a BIF such as %int.
Create a file named io_input.rpgle:
**FREE
dcl-s custName char(20);
dcl-s ageChar char(3);
dcl-s age int(5);
dcl-s msg char(52);
// DSPLY with a response field prompts for and reads input
dsply 'Enter your name:' '' custName;
dsply 'Enter your age:' '' ageChar;
// Convert the character input to a number
age = %int(ageChar);
msg = 'Hello, ' + %trim(custName) + '!';
dsply msg;
if age >= 18;
msg = 'Status: adult';
else;
msg = 'Status: minor';
endif;
dsply msg;
*inlr = *on;
Key points:
- The first operand is the prompt message, the second (
'') is the message queue (blank uses the default external message queue), and the third is the response field that receives the typed value. - Input always arrives as character data - use
%int,%dec, or%dateto convert it to the type you need. - For real interactive screens, production RPG typically uses display files (DDS-described 5250 screens) with
read/write, butdsplyis perfect for simple prompts and learning.
Reading Database Files
Database file access is the heart of RPG. A physical file (table) is externally described: you declare it with dcl-f, and its fields automatically become program variables. Records are read sequentially with read or retrieved by key with chain.
Create a file named io_readfile.rpgle:
**FREE
// CUSTOMER is an externally described, keyed physical file with fields:
// CUSTID packed(7:0) - key
// CUSTNAME char(30)
// BALANCE packed(11:2)
dcl-f customer usage(*input) keyed;
dcl-s msg char(60);
// Sequential read of every record until end of file
read customer;
dow not %eof(customer);
msg = %char(custid) + ' ' + %trim(custname);
dsply msg;
read customer;
enddow;
// Random access by key with CHAIN
chain 1001 customer;
if %found(customer);
msg = 'Found: ' + %trim(custname);
else;
msg = 'Customer 1001 not found';
endif;
dsply msg;
*inlr = *on;
Key points:
dcl-f customer usage(*input) keyeddeclares an input-only keyed file. The record’s fields (custid,custname,balance) exist as program variables with no extra declaration.readfetches the next record;%eofreturns*onwhen there are no more.chaindoes a keyed random read -chain 1001 customerlooks up the record whose key is1001.%foundreports whether it succeeded.dow(do-while) withnot %eof(...)is the idiomatic read loop in free-form RPG.
Writing a Printer Report
RPG earned its name generating printed reports. On IBM i, report output goes to a printer file whose layout is described in DDS with named record formats (a heading format, a detail format). The program moves data into the format’s fields and issues write, with an overflow indicator handling page breaks.
Create a file named io_report.rpgle:
**FREE
// SALESRPT is an externally described printer file (defined in DDS)
// with record formats HEADING and DETAIL. The DETAIL format contains
// the output fields PRODUCT (char 20), QTY (int) and AMOUNT (packed 9,2).
dcl-f salesrpt printer oflind(*in90);
dcl-s names char(20) dim(3);
dcl-s units int(10) dim(3);
dcl-s prices packed(9:2) dim(3);
dcl-s i int(10);
names(1) = 'Widget'; units(1) = 100; prices(1) = 4.99;
names(2) = 'Gadget'; units(2) = 50; prices(2) = 9.99;
names(3) = 'Gizmo'; units(3) = 25; prices(3) = 14.99;
// Write the page heading
write heading;
// Write one detail line per product
for i = 1 to 3;
// Reprint the heading if the page overflowed
if *in90;
write heading;
*in90 = *off;
endif;
product = names(i); // fields defined in the DETAIL DDS format
qty = units(i);
amount = prices(i);
write detail;
endfor;
*inlr = *on;
Key points:
dcl-f salesrpt printer oflind(*in90)declares an externally described printer file and names*in90as the overflow indicator, which turns on when the page fills.write headingandwrite detailemit records using DDS-defined formats; the program only supplies the field values.- Output does not go to the screen - it becomes a spooled file on the output queue, viewable with the
WRKSPLFcommand. - This record-and-format model is exactly how RPG has produced business reports since 1959.
Database I/O with Embedded SQL
Modern RPG mixes native record I/O with embedded SQL for DB2 for i. SQL statements are written directly in the program between exec sql and a semicolon. RPG variables become host variables by prefixing them with a colon, and sqlcode reports the result of each statement.
Create a file named io_sql.rpgle:
**FREE
ctl-opt dftactgrp(*no);
dcl-s custName varchar(30);
dcl-s custBal packed(11:2);
dcl-s totalBal packed(13:2);
dcl-s msg char(52);
// Single-row read with SELECT INTO
exec sql
SELECT custname, balance INTO :custName, :custBal
FROM customer
WHERE custid = 1001;
if sqlcode = 0;
msg = %trim(custName) + ': ' + %trim(%editc(custBal:'1'));
dsply msg;
elseif sqlcode = 100;
dsply 'Customer 1001 not found';
endif;
// Aggregate query
exec sql
SELECT SUM(balance) INTO :totalBal
FROM customer;
msg = 'Total balance: ' + %trim(%editc(totalBal:'1'));
dsply msg;
// Read many rows with a cursor
exec sql DECLARE c1 CURSOR FOR
SELECT custname FROM customer ORDER BY custname;
exec sql OPEN c1;
exec sql FETCH c1 INTO :custName;
dow sqlcode = 0;
dsply custName;
exec sql FETCH c1 INTO :custName;
enddow;
exec sql CLOSE c1;
// Write a new row with INSERT
exec sql
INSERT INTO customer (custid, custname, balance)
VALUES (2001, 'New Customer', 0);
if sqlcode = 0;
dsply 'Row inserted';
endif;
*inlr = *on;
Key points:
exec sql ... ;embeds a SQL statement; host variables are RPG fields prefixed with:.sqlcodeis0on success,100when aSELECT/FETCHfinds no (more) rows, and negative on error.- A cursor (
DECLARE,OPEN,FETCH,CLOSE) reads a result set row by row - the SQL equivalent of the nativereadloop. - Because DB2 is built into IBM i,
INSERT,UPDATE, andDELETEare as natural in RPG as nativewriteandupdate.
Running on IBM i
RPG requires an IBM i system - there is no open-source compiler for Linux, macOS, or Windows, and no Docker image is available. The database examples also require the CUSTOMER file (and, for the report, the SALESRPT printer file) to exist first.
Create the sample table with SQL from an interactive session or a script:
| |
Compile and run a plain RPG program (no embedded SQL) with CL commands:
| |
For programs that contain embedded SQL, use the SQL precompiler instead:
| |
You can get IBM i access through PUB400.COM (a free public learning system), IBM Power Virtual Server, or IBM i Access Client Solutions.
Expected Output
Console output from io_console.rpgle:
Order Report
Quantity: 5
Unit price: 1,234.56
Total: 6,172.80
Interactive session for io_input.rpgle (assuming the user types Alice and 30):
Enter your name: Alice
Enter your age: 30
Hello, Alice!
Status: adult
Reading the sample CUSTOMER file with io_readfile.rpgle:
1001 Acme Corp
1002 Globex Inc
Found: Acme Corp
The spooled report from io_report.rpgle (viewed with WRKSPLF):
SALES REPORT
Product Qty Amount
Widget 100 4.99
Gadget 50 9.99
Gizmo 25 14.99
Embedded SQL output from io_sql.rpgle:
Acme Corp: 2,500.00
Total balance: 4,250.50
Acme Corp
Globex Inc
Row inserted
On IBM i,
DSPLYmessages appear in the job log prefixed withDSPLY(for exampleDSPLY Order Report). Interactively they pause for Enter.
Key Concepts
- RPG I/O is record-based - files are described externally and their fields become program variables, reflecting RPG’s origins as the Report Program Generator.
dsplyhandles simple console output and, with a response field, simple input; production screens use DDS display files.read/chainread database records sequentially or by key, with%eofand%foundreporting the result.writeemits output records, including printer-file formats that produce spooled reports.- Built-in functions format output:
%charfor plain conversion,%editcfor business editing (commas and decimals),%trimto clean up blanks. - Embedded SQL (
exec sql) integrates DB2 for i directly into RPG, using colon-prefixed host variables andsqlcodefor status. - Cursors (
DECLARE/OPEN/FETCH/CLOSE) read multi-row result sets, the SQL counterpart of the native read loop. - Everything runs only on IBM i - compile with
CRTBNDRPG, orCRTSQLRPGIwhen embedded SQL is present.
Comments
Loading comments...
Leave a Comment