We often create ILE RPG applications for our customers to provide the server-side logic and database access for IBM i mobile applications—which we use with ASNA Mobile RPG. We've become big fans of the TR7-introduced full free-format RPG and embedded SQL. The combination of a moderately rational RPG and SQL database access make a great duo for creating effective, performant server-side components.

In a pure world, these applications would be architected with ILE RPG's service programs (and binding directories and other ILE composite goodies). However, we generally hand these finished applications off to customer RPG teams with little or no advanced ILE RPG knowledge. We need to keep these applications simple so we usually use /COPY instead of other ILE RPG partitioning schemes.

We learned a lesson the other day about using /COPY included files with embedded SQL. Consider the following two ILE RPG snippets:

Dcl-DS CustomerList  Dim(50) Qualified;
    CMCustNo     Int(10);
    CMName       Char(40);
    CMCity       Char(30);
    CMState      Char(2);
End-DS; 

Figure 1a. A data structure area definition named CUSTLISTDS and stored in the INCLUDES source physical file.

/COPY INCLUDES,CUSTLISTDS        

EXEC SQL
    DECLARE C_DOC1 CURSOR FOR
    SELECT CMCustNo,
        Trim(CMName), Trim(CMCity), CMState
        FROM Customer   
        ORDER BY LCASE(TRIM(CMName))
        FOR READ ONLY;                   
EXEC SQL
    OPEN C_DOC1;
EXEC SQL
    FETCH C_DOC1 FOR 12 ROWS INTO :CustomerList;
EXEC SQL 
    CLOSE C_DOC1;

Figure 1b. Embedded SQL in RPG putting a result set into the host structure named CustomerList.

The CustomerList data structure array defines an SQL host structure (a replaceable SQL token). In Figure 1b, the SQL statement:

FETCH C_DOC1 FOR 12 ROWS INTO :CustomerList;

puts the fetched result set into the CustomerList host structure.

Using CRTSQLRPGI to compile the RPG program that contained the snippet shown in Figure 1b produced an SQL SQL5011 error ("Host structure array CustomerList was not defined or not usable"). When a member included with /COPY defines a host structure it must be available to the SQL precompiler, and this apparently was not happening.

The fix to this issue was simple, but moderately elusive. Scouring the Database Embedded SQL Programming manual provided a clue (the text in the manual is somewhat ambiguous) regarding CRTSQLRPGI's RPGPPOPT parameter. Providing *LVL1 or *LVL2 as the value for CRTSQLRPGI's RPGPPOPT parameter (as shown below in Figure 3), fixed the problem.

CRTSQLRPGI OBJ(*LIBL/CSTLST) 
           RPGPPOPT(*LVL1)

There is also an /INCLUDE directive available, but it only works when RPGPPOPT = *LVL2.  

One other wrinkle

The Database Embedded SQL Programming manual includes an ominous warning about using *LVL1 and *LVL2 values with RPGPPOPT:

"When *LVL1 or *LVL2 is used, there is a possibility that the expanded source generated by the RPG preprocessor will become very large and reach a resource limit due to the expansion of the /COPY and /INCLUDE statements. If this happens you must either break up your source into smaller pieces, or not use the RPG preprocessor."

Resource limit? We're on an IBM i! Not Windows. Oh, well. We're taking our chances and going to stick with /COPY and RPGPPOPT = *LVL1. However, the Database Embedded SQL Programming manual also briefly mentions the SQL INCLUDE statement. We found that we could swap out

/COPY INCLUDES,CUSTLISTDS 

for

EXEC SQL
    INCLUDE CustListDS;

and use this call to CRTSQLRPGI

CRTSQLRPGI OBJ(RPDREAM/CSTLST) INCFILE(INCLUDES) 

where the INCFILE parameter specifies the source physical file in which to find external members. This compile also worked. We're not going to use this; /COPY works fine for us. However, this SQL INCLUDE may be a work-around should you ever bump into IBM's ominous warning issued about reaching "a resource limit" with the RPGPPOPT parameter. If you do use the SQL INCLUDE statement note that all included members must reside in the same source physical file.

Summary

If you need to include RPG members that define host structures needed by the SQL precompiler, use /COPY with CRTSQLRPGI's RPGPPOPT = *LVL1 or *LVL2. You can probably safely ignore the /INCLUDE directive. Keep the SQL INCLUDE statement in mind should you ever exceed resource limits with the /COPY directive.