Embedding SQL
in RPG Programs
Susan Gantner
[email protected]
www.Partner400.com
www.SystemiDeveloper.com Your partner in System i Education
Many RPG programmers have used interactive SQL as a tool to quickly browse data or to create
test data scenarios, but have stopped short of embedding it into their RPG programs. Come to this
session to learn why, when and how you may want to use SQL in conjunction with RPG. We will
cover the basics of embedding SQL into RPG, including using SQL cursors.
The authors, Susan Gantner and Jon Paris, are co-founders of Partner400, a firm specializing in
customized education and mentoring services for AS/400 and iSeries developers. After many years
with IBM, including several years at the Rochester and Toronto laboratories, Jon and Susan are
now devoted to educating developers on techniques and technologies to extend and modernize
their applications and development environments. This is done via on-site custom classes for
individual companies as well as conferences and user group events.
Together with her partner, Jon Paris, Susan authors regular technical articles for the IBM
publication, IBM Systems Magazine, i5 edition (formerly iSeries Magazine and eServer Magazine,
iSeries edition), and the companion electronic newsletter, i5 EXTRA (formerly iSeries Extra). You
may view articles in current and past issues and/or subscribe to the free newsletter or the magazine
at: http://www.ibmsystemsmag.com.
Susan and Jon are also partners in SystemiDeveloper, a company that hosts the RPG Summit and
DB2 Summit conferences. See SystemiDeveloper.com for more details.
This presentation may contain small code examples that are furnished as simple examples to
provide an illustration. These examples have not been thoroughly tested under all conditions. We
therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.
All code examples contained herein are provided to you "as is". THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED.
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 1-2 .
Agenda
Quick review: What is SQL?
Syntax and rules for embedding SQL into programs
Using SQL cursors for processing individual rows in a set
Error detection and handling
Tips
Dynamic SQL (appendix only)
Types of SQL Statements
Data Manipulation Language (DML)
SELECT - retrieves data; one row or multiple
UPDATE - updates one row or multiple
DELETE - deletes one row or multiple
INSERT - adds one row or multiple
Data Definition Language (DDL)
CREATE - creates schemas, tables, views, etc.
DROP - deletes schemas, tables, views, etc.
ALTER - change the format or attributes of a table
Data Control Language (DCL)
GRANT & REVOKE - manages authorities to DB objects
Programming Statements
DESCRIBE, PREPARE - used for dynamic embedded SQL
Logic control statements for SQL procedures, triggers and functions
(SPL)
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 3-4 .
Using SQL on System i
Interactive SQL - Use STRSQL command
Targeted primarily to programmers
Embedded SQL - Put into High Level Language (HLL)
Such as RPG or COBOL
Query Manager - Report Formatter
Targeted primarily to end users
An alternative to Query/400
iSeries Navigator
SQL wizards, productivity and testing tools
Graphical alternative to Interactive SQL
SQL Stored Procedures and SQL Triggers
Enforce business rules using SQL-based logic
Quick Review of SQL SELECT Syntax
Enter SQL Statements
Type SQL statement, press Enter.
> SELECT * FROM users WHERE clue > 0________
_0 rows returned._________________________
__________________________________________
__________________________________________
__________________________________________
__________________________________________
__________________________________________
__________________________________________
Bottom
F14=Delete line F15=Split line F16=Select library
F17=Select files F18=Select fields F24=More keys
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 5-6 .
RPG Interface - Source
Retrieve column/field values into program variables
One-to-one correspondence between SELECT and INTO lists
SELECT....INTO expects only a SINGLE row/record
Multiple rows require the use of cursor operations
Note: /Free form allows embedded SQL only V5R4 and later
* No F spec needed !
D EmpNbr S 5 0
D Name S 25
D Job S 1
C/EXEC SQL
C+ SELECT NAME, POS
C+ INTO :Name, :Job
C+ FROM EMPL
C+ WHERE NBR = :EmpNbr
C/END-EXEC
/Free
EXEC SQL SELECT NAME, POS
INTO :Name, :Job FROM EMPL
WHERE NBR = :EmpNbr;
Rules: Embedding SQL in RPG Code
All SQL statements must be coded on a C spec
SQL statements begin with /EXEC SQL in positions 7-15
with the slash in position 7 (except in /Free form V5R4+)
... and end with /END-EXEC in positions 7-15 (except in /Free)
SQL statements may appear on the same line as /EXEC SQL
/END-EXEC must be on a separate line
Between beginning and ending delimiters, all SQL
statements must have + in position 7 (except in /Free form
V5R4+)
Note: SQL statements CANNOT be coded in the /FREE parts
of your C specs until V5R4
The SQL statement itself is free-form within the limitations listed here
Comments may be included at end of line preceded with --
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 7-8 .
More Rules: Embedding SQL in RPG
Program variables referenced in the SQL statement are
preceded with a colon (e.g., :EmpNbr)
Must not have names beginning with SQ, SQL, RDI, or DSN
Names must not be > 64 characters long
If local variables have same names as other locals or globals, all
definitions of that name must be identical
In V5R4, they need only be of the same type (e.g., numeric, character,
date) but will still issue Severity 11 warning
Set GENLVL parameter to be 11 or higher to have them accepted
SQL statements cannot be included via a /COPY statement
However, you can use the SQL INCLUDE statement instead
C/Exec SQL
C+ Include SrcMbrName
C/End-Exec
SELECT INTO
SELECT INTO SQL Statement is used to retrieve a single
record from the database
If your statement returns more than 1 row, you must use another form
of SELECT we will discuss later
D CustID S 7P 0
D CustName S 30A
D CustSales S 7P 0
C/EXEC SQL
C+ SELECT CUSTID,CUSTNAM, CUSTSALES
C+ INTO :CUSTID,:CUSTNAME,:CUSTSALES
C+ FROM CUSTOMER
C+ WHERE CUSTID = :InputCust
C/END-EXEC
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 9-10 .
Updates and Deletes
SQL updates and deletes can be embedded as well
Set-at-a-time updates do not require cursor operations
This statement will update ALL records that meet the Where criteria
C/EXEC SQL
C+ UPDATE empl
C+ SET sal = sal + (sal * .10)
C+ WHERE pos = 5
C/END-EXEC
Using Structures in SQL
Host structures are groups of variables
Data structures in RPG
Structures can be used in SQL statements
Replaces list of variables
DS subfields must contain the correct number, type and sequence for
SELECT columns
D CustInfo DS
D CustID 7P 0
D CustName 30A
D CustSales 7P 0
D InputCust S 7P 0
C/EXEC SQL
C+ SELECT CUSTID, CUSTNAM, CUSTSALES
C+ INTO :CustInfo
C+ FROM CUSTOMER
C+ WHERE CUSTID = :InputCust
C/END-EXEC
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 11-12 .
External Data Structures
The simple way to define a complete record format to match
a SELECT * request
Declare an externally described Data Structure
Subfields will be brought in to match all the fields in the record format
D CustRec E DS ExtName(Customer)
D InputCust S 7P 0
C/EXEC SQL
C+ SELECT *
C+ INTO :CustRec
C+ FROM CUSTOMER
C+ WHERE CUSTID = :InputCust
C/END-EXEC
SQL Result Sets
Group of rows that are derived as a result of an SQL query.
The answer to your SQL question
Made up of rows and columns
SELECT nbr, name
SQL Statement
FROM empl Produces
WHERE pos = 5
Nbr
10
Name
AMY
Pos
2
Sex
F
Sal
1200
Result Set
35 JOE 5 M 1000 Nbr Name
30 JON 7 M 1500 35 JOE
20 DON 5 M 1150 20 DON
25 ANN 8 F 1550
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 13-14 .
Selecting & Processing Multiple Rows
Steps to access multiple rows:
1. Declare cursor
2. Open cursor
3. Fetch a row (record)
4. Process row (UPDATE, INSERT, etc)
5. IF last row: go to Step 6,
ELSE go to Step 3
6. Close cursor
DECLARE CURSOR statement
Similar in function to HLL file declarations (F-specs or FD's)
No processing actually takes place - just definition
Host variables may be included in the statement
Created using an embedded SELECT command
most SELECT clauses may be used - ORDER BY, GROUP BY, etc
Must be declared before being referenced
"Before" meaning physically present in the source member earlier
If updating fields/columns, specify "For Update Of ..." clause
List the column(s) that may be updated using this cursor
C/EXEC SQL
C+ DECLARE empcsr CURSOR FOR
C+ SELECT nbr, nam, sal
C+ FROM emp
C+ WHERE dpt = :dept
C+ FOR UPDATE OF sal
C/END-EXEC
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 15-16 .
OPEN statement
Actually executes the SQL Select statement
Builds the access path if necessary
Successful Open places the file cursor before the first row of
the result set
Cursor must be closed before it can be re-opened
C/EXEC SQL
C+ OPEN empcsr
C/END-EXEC
FETCH statement:
Bring rows into the program
One at a time for processing
Fields placed into host variables
one for one with fields in SELECT
Fetch Next is the default behavior
If a cursor is declared as "scrollable", Fetch has other options
e.g., Prior, First, Last
C/EXEC SQL
C+ FETCH NEXT FROM empcsr
C+ INTO :number, :name, :salary
C/END-EXEC
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 17-18 .
Positioned Update and Delete Stmts
Update or delete the current row of an updatable cursor
Can only be done after successful Fetch operation
Add a "Where Current of" clause to the Update and Delete statements
C/EXEC SQL
C+ DECLARE empcsr CURSOR FOR
C+ SELECT nbr, nam, sal
C+ FROM emp ORDER BY nbr
C+ FOR UPDATE OF sal
C/END-EXEC
C/EXEC SQL OPEN empcsr
C/END-EXEC
C/EXEC SQL
C+ FETCH NEXT FROM empcsr
C+ INTO :number, :name, :salary
C/END-EXEC
C/EXEC SQL
C+ UPDATE emp
C+ SET sal = sal + :raise
C+ WHERE CURRENT OF empcsr
C/END-EXEC
Close Statement
Close the cursor
Cursor must be opened in order to be closed
DB2/400 may close cursors for other reasons also:
job end
activation group ends
program ends
modules ends
commit or rollback without a 'with hold' clause
error handling......
C/EXEC SQL
C+ CLOSE empcsr
C/END-EXEC
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 19-20 .
SQL Precompiler for Embedded SQL
User Modified
Source Precompile Source Compile Program
Member Member
Processed Access
SQL Stmts Plans
(temporary)
Warning for Leading Edge RPGers
The SQL precompiler does not fully understand or support all
the latest RPG language features
V5R4 allows embedded SQL in /Free calcs and relaxed local variable
naming rules
Examples of things not supported as of V5R3 (not a complete list)
SQL embedded in /Free RPG calcs
> 1 level of qualification for DS subfields
Proper recognition of local variables in subprocedures
Examples of things not supported UNTIL V5R3 (not a complete list)
DS arrays for blocked fetches or inserts
One level of simple DS subfield qualification (not for DS array)
LIKEDS and LIKEREC support (2nd parameter still not supported)
See the iSeries Information Center for details on limitations
of RPG IV support
Topic is "Embedded SQL Programming" under
"Database/Programming" topic
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 21-22 .
Creating Programs with SQL Stmts
Create RPG/400 SQL applications using CRTSQLRPG
Creates a *PGM object
Create RPG IV SQL applications using CRTSQLRPGI
Creates either a *PGM, *MODULE, or *SRVPGM object
Compiling RPG/400 SQL Programs
Create SQL RPG Program (CRTSQLRPG)
Program . . . . . . . ________ Name
Library . . . . . . *CURLIB Name, *CURLIB
Source file . . . . . QRPGSRC Name, QRPGSRC
Library . . . . . . *LIBL Name, *LIBL, *CURLIB
Source member . . . . *PGM Name, *PGM
Commitment control . . *CHG *CHG, *ALL, *CS, *NONE...
Relational database . *LOCAL
Text 'description' . . *SRCMBRTXT
Precompiler options . *NOSRC__ *SRC, *SOURCE, *NOSRC...
+ for more values _______
Target release . . . *CURRENT *CURRENT, *PRV, V4R4M0, ...
INCLUDE file . . . . *SRCFILE Name, *SRCFILE
Library . . . . . *LIBL Name, *LIBL, *CURLIB
. . . .
To source file . . . . QSQLTEMP Name
Library . . . . . . QTEMP Name, *LIBL, *CURLIB
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 23-24 .
Compiling RPG IV SQL Programs
Create SQL ILE RPG Object (CRTSQLRPGI)
Object . . . . . . . _________ Name
Library . . . . . *CURLIB Name, *CURLIB
Source file . . . . QRPGLESRC Name, QRPGLESRC
Library . . . . . *LIBL Name, *LIBL, *CURLIB
Source member . . . *OBJ Name, *OBJ
Commitment control . *CHG *CHG, *ALL, *CS, *NONE...
Relational database *LOCAL
Compile type . . . . *PGM *PGM, *SRVPGM, *MODULE
Listing output . . . *NONE *NONE, *PRINT
Text 'description' . *SRCMBRTXT
Precompiler options. *XREF *XREF, *NOXREF, *GEN...
+ for more values ________
Target release . . . *CURRENT *CURRENT, *PRV, V4R4M0, ...
INCLUDE file . . . . *SRCFILE Name, *SRCFILE
Library . . . . . *LIBL Name, *LIBL, *CURLIB
. . . . .
Close SQL cursor . . *ENDACTGRP *ENDMOD, *ENDACTGRP
. . . . .
Debugging view . . . *NONE *SOURCE, *NONE
To source file . . . QSQLTEMP Name
Library . . . . . QTEMP Name, *LIBL, *CURLIB
SQL and Commitment Control
Note that default on compiles is Commit *CHG
This means:
If files aren’t journaled, programs will abnormally terminate whenever
you attempt to use SQL on those files
If your files are journaled, you will have uncommitted transactions
unless you remember to explicitly commit them
If your shop does not normally use commitment control, consider using
Commit *NONE on your compile commands
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 25-26 .
Debugging Embedded SQL
When using ILE source view debugger
ILE SQL program compiles automatically generate 3 different views
with DBGVIEW(*SOURCE):
Use F15 to switch between:
SQL Root Source view
SQL Output view (output from SQL precompiler)
Listing view
V5R3+ Compile Enhancement
What about compile options that are valid on CRTRPGPGM
or CRTBNDRPG or CRTRPGMOD that are NOT present on the
corresponding SQL compile statements?
With SQLRPGLE members, the best way to handle this is by
embedding the compile options on the H spec
e.g., H DFTACTGRP(*No) ACTGRP(MYAG) BNDDIR('MYBNDDIR')
This option has been available for many releases
New parameter added in V5R3:
Works for other source types (e.g., SQLRPG, SQLCBL)
Also works for SQLRPGLE types if recommended H spec option is not used
Compiler Options (COMPILEOPT)
A 5,000 character parameter field allowing parameters to be passed on to
the language compiler after the SQL precompile step
Parameters that are included on the SQL compile already cannot be
specified here because it will result in duplicate parameters being specified
Use compile keywords with the values
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 27-28 .
Error Detection and Handling
Status always returned in the code
both successful and unsuccessful statements
Programmer must check return codes within program
SQL Communications Area (SQLCA)
contains feedback information
must be included in all SQL programs
RPG includes SQLCA automatically
COBOL programs must have specific include, as shown below:
/EXEC SQL
INCLUDE SQLCA
/END-EXEC
Error Detection and Handling
SQL Communications Area (SQLCA)
SQLCAID Char(8) Structure identifying literal: "SQLCA"
SQLCABC Integer Length of SQLCA
SQLCode Integer Return code
SQLErrML SmallInt Length of SQLErrMC
SQLErrMC Char(70) Message Replacement text
SQLErrP Char(8) Product ID literal: "QSQ" for DB2/400
SQLErrD Array of Integers SQLErrD(1) - treated as Char(4); last 4 characters of
CPF or other escape message
SQLErrD(2) - treated as Char(4); last 4 characters of
CPF or other diagnostic message
SQLErrD(3) - for Fetch, Insert, Update or Delete,
number of rows retrieved or updated
SQLErrD(4) - for Prepare, relative number indicating
resources required for execution
SQLErrD(5) - for multiple-row Fetch, contains 100 if
last available row is fetched; for Delete,
number of rows affected by referential
constraints; for Connect or Set
Connection, contains t-1 if unconnected,
0 if local and 1 if connection is remote
SQLErrD(6) - when SQLCode is 0, contains SQL
completion message id
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 29-30 .
Error Detection and Handling
SQL Communications Area (SQLCA) continued
SQLWarn Char(11) Set of 11 warning indicators; each is blank, W, or N
SQLWarn0 Char(1) Blank if all other SQLWARNx warning indicators are blank
W if any warning indicator contains W or N
SQLWarn1 Char(1) W if a string column was truncated when assigned to host variable
SQLWarn2 Char(1) W if null values were eliminated from a function
SQLWarn3 Char(1) W if number of columns is larger than number of host variables
SQLWarn4 Char(1) W if prepared Update or Delete statement has no a Where clause
SQLWarn5 Char(1) Reserved
SQLWarn6 Char(1) W if date arithmetic results in end-of-month adjustment
SQLWarn7 Char(1) Reserved
SQLWarn8 Char(1) W if result of character conversion contains the substitution character
SQLWarn9 Char(1) Reserved
SQLWarnA Char(1) Reserved
SQLState Char(5) Return code; "00000" if no error or warning
SQLCODE Error Handling
SQLCODE (SQLCOD in RPG) contains return code
= 0 Successful statement execution
> 0 Successful, with warning condition
< 0 Unsuccessful - statement failed
SQLCODE value indicates exact error or condition
e.g.. 100 = Row not found (or end of file)
e.g.. -552 = Not authorized to object
SQLCODE values have corresponding messages
e.g.. SQL0100 = Row not found
e.g.. SQL0552 = Not authorized to &1.
Many SQL pgmrs also check SQLWN0
A few warning conditions do not return > 0 SQLCODE value
e.g., If SQLCOD > 0 or SQLWN0 <> *Blank
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 31-32 .
SQLCODE and Messages
Most SQLCODE values map to message IDs in QSQLMSG
message file
MSGID is SQxxxxx where xxxxx is derived with the following rules:
1. Calculate the absolute value of the SQLCODE value.
2. Zero-fill the result with leading zeros to construct a 5-digit number.
3. If the first digit in this number is 0, replace it with the letter 'L'.
For example, SQLCODE value of -151 = message ID SQL0151
SQLCODE value of -30090 = message ID SQ30090.
Use DSPMSGD to quickly find meanings of SQLCODE values
SQLCODE and SQLSTATE
SQLSTT or
SQLCOD or
Condition SQLSTATE Class
SQLCODE
(1st 2 positions)
Successful 0 '00'
Warning >0 '01'
End of Data
100 '02'
Row not found
Error <0 >='03'
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 33-34 .
SQLCODE vs. SQLSTATE
SQLCODE values vary by platform
By contrast, SQLSTATE values are standard across platforms
However, SQLCODE is most widely used, especially for individual
statements
SQLCODE is a deprecated function in current SQL standard
"Deprecated" means function replaced with a newer method
Use of deprecated functions is typically discouraged
DB2 UDB for iSeries plans to continue to support SQLCODE
for the forseeable future
Error Checking With SQLCOD
C/EXEC SQL
C+ SELECT name INTO :nam
C+ WHERE emp = :number
C/END-EXEC
C Select
C When SQLCod < 0
C ExSR SQLError
C When SQLCod = 100
C ExSR NoMoreRows
C When ( SQLCod > 0 Or SQLWn0 <> *Blank )
C ExSR SQLWarning
C Other
C ExSR ProcessRow
C EndSL
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 35-36 .
Error Checking With SQLSTT
C/EXEC SQL
C+ SELECT name INTO :nam
C+ WHERE emp = :number
C/END-EXEC
C Select
C When %Subst(SQLStt:1:2) >= '03'
C ExSR SQLError
C When %Subst(SQLStt:1:2) = '02'
C ExSR NoMoreRows
C When %Subst(SQLStt:1:2) = '01'
C ExSR SQLWarning
C Other
C ExSR ProcessRow
C EndSL
Embedded SQL Tips
Use SQL source member types
e.g., SQLRPG, SQLRPGLE, SQLCBL, SQLCBLLE
Prompting won't work without SQL member type
You can prompt SQL statements in SEU
You MUST key both /EXEC SQL and /END-EXEC statements first
Enter the C+ for the SQL statement
Then you can prompt (F4) for statements in between
Same prompter as interactive SQL
To help diagnose run-time or performance problems
Look in your job log after running the SQL program
Messages there often help diagnose problems
Put your job in debug mode before running SQL program, then look in
your job log
Additional messages are put in job log when in debug mode which can
be helpful in diagnosing SQL performance problems
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 37-38 .
Static vs. Dynamic SQL
Static SQL
SQL Statement structure known at compile time
Better Performance
Dynamic SQL
SQL Statements built during program execution
Greater Flexibility
A few charts in the appendix of this handout illustrate the
basics of using Dynamic SQL
Summary
Practical, effective solution for applications requiring
complex data retrieval
Very flexible, functional application development tool
embedded in a HLL program
or entered interactively
Portability to other relational databases
Easy report writing with programmable flexibility
Similar skills across many relational databases
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 39-40 .
What is Dynamic SQL?
A different way to use SQL
SQL statements are not predefined in program
Dynamically created on the fly as part of program logic
SQL Precompiler cannot fully process dynamically created
SQL statements
PREPARE statement is used in program logic to compile dynamically
created SQL statements at run time
Simple dynamic SQL statement process:
Build SQL statement in a character variable
PREPARE the SQL statement
EXECUTE the SQL statement
Where to use Dynamic SQL
Report programs with user run time selection
Files
Fields
Record selection criteria
Sorting
SQL built in functions
Whenever the exact syntax of an SQL statement cannot be
determined beforehand
Dynamic SQL can be resource intensive
A dynamic SQL statement has to be parsed (interpreted) and
executed at run time
Negative performance impact
Use dynamic SQL only when necessary
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 41-42 .
Parameter Markers in Dynamic SQL
Dynamic SQL statements cannot contain host variables
e.g., :CUSTNO
Parameter markers are placed in embedded SQL statements
Indicated by ?
Used to dynamically insert host variable data for predicate values
and/or column assignments
Multiple markers and values can be assigned
Values are assigned to markers when the statement is executed
Example on next chart
C Eval SQLStmtFld = 'Delete From Customer Where -
C CUSTNO = ?'
Dynamic SQL - Example
C If DeleteCorp
C Eval Condition = 'Corp = ?'
C Else
C Eval Condition = 'CustNo = ?'
C EndIf
C Eval SQLStmtFld = 'Delete From Customer Where '
C + Condition
C/EXEC SQL
C+ PREPARE DynSQLStmt
C+ FROM :SQLStmtFld
C/END-EXEC
C If (SQLCod = 0) And (SQLWn0 = *Blank)
C/EXEC SQL
C+ EXECUTE DynSQLStmt
C+ Using :Cust
C/END-EXEC
C EndIf
©Copyright Partner400, 2003 - 2007. SQL in RPG - Page 43-44 .