close window

Print print

D-spec Discoveries

Do you think you know everything there is to know about using D-specs in RPGIV? Read on and you may find a few things that surprise you.

The inspiration for this column comes from one of the most commonly asked questions on RPG programming lists: "How do I directly load fields from a database record into an array?" The question normally arises when each database record contains a series of related values-for example, monthly sales figures. Yes, we know that a normalized database wouldn't contain such a thing, but we're talking real life here.

For these examples, we'll use the following DDS for the physical file:

 * Sales per quarter by Customer
          R SALESREC1
            CUSTNO         4
            DIVISION       2
            Q1             7  2
            Q2             7  2
            Q3             7  2
            Q4             7  2
          K CUSTNO

Our objective is to access the individual fields Q1-Q4 as an array of four elements.

One approach is to move each field in the record into its corresponding array element. In this example, with only four fields, this wouldn't be too arduous. But, what if there were 12 or 20 fields? Regardless of the number, it's inefficient and better solutions are available, including several that involve some little-known features of D-specs.

Let's start with the simplest and, perhaps, most obvious option. Define an externally described Data Structure (DS) using the record format to supply the description (A). Then use the OVERLAY keyword to create the array (B).

(A)  D SalesData     E DS                  ExtName(TestFile)

      // Define array over Quarterly Sales figures

(B)  D  SalesForQtr                        Overlay(SalesData: 7)
(C)  D                                     Like(Q1) Dim(4)


Notice the use of the LIKE keyword (C), which ensures that the array elements definition matches that used for the Q1 field.


This technique works well when the array fields start at the beginning of the record. In this case, however, the Customer number (CUSTNO) and Division code (DIVISION) get in the way, preventing us from simply saying Overlay(SalesData). Instead we had to say that the array started at the 7th position in the DS (i.e., Overlay(SalesData: 7)).

The only thing wrong with that is that we must calculate the number of characters that precede the array. The potential problem arises if the record layout changes in the future-perhaps by adding another digit to the Customer number or inserting an extra field after the Division code. In both cases, we'd need to remember to modify the start position on the overlay to accommodate the additional characters. One of the D-specs strongest features is that we don't need to know all of the positional information that was required in the old days of using I-specs. Consequently this seems like a step backward. Is there a better way?

Yes-and this second example demonstrates it nicely:

      // This method uses the file's field names - and takes
      //   advantage of the fact that a length is not required.
     D SalesData       DS
(D)  D  Q1
(D)  D  Q2
(D)  D  Q3
(D)  D  Q4
(E)  D  SalesForQtr                       Overlay(SalesData)
     D                                    Like(Q1) Dim(4)

Notice that we've incorporated the Quarterly sales fields into the DS by specifying their names (D). No length or type definition is required. Instead of allocating storage for the files fields arbitrarily, the compiler is told to explicitly place the data in the DS. Because the DS no longer contains the Customer and Division data, we can use the simple form of the Overlay keyword (E). Unlike the externally described DS, you see exactly which fields form the DS. In addition, the fields in the DS can come from multiple files.

This next method is similar to the externally described DS approach used in the first example, except were taking advantage of another little-known D-spec feature-the use of group field names. In the following example, QuarterData (F) is a group field and can, therefore, be used as the target for the OVERLAY keyword (H).

     D SalesData     DS
     D  CustNo
     D  Division
(F)  D  QuarterData
(G)  D    Q1                            Overlay(QuarterData)
(G)  D    Q2                            Overlay(QuarterData: *Next)
(G)  D    Q3                            Overlay(QuarterData: *Next)
(G)  D    Q4                            Overlay(QuarterData: *Next)

(H)  D  SalesForQtr                     Overlay(QuarterData)
     D                                  Like(Q1) Dim(4)

Note that QuarterData has no length, no type definition and no LIKE keyword, and doesn't exist in any of the programs files. Normally you'd expect such a definition to result in a Field Not Defined error, but it doesn't because the subsequent OVERLAY references (G) inform the compiler that QuarterData represents a group field that comprises the fields Q1 though Q4. If you examine the extract from the compiler cross-reference listing below, you'll see that the field is defined as 28 characters long (J) (i.e., the combined lengths of Q1, Q2, Q3 and Q4):

          Q1                S(7,2)                     14D . . . .
          Q2                S(7,2)                     15D . . . .
          Q3                S(7,2)                     16D . . . .
          Q4                S(7,2)                     17D . . . .
(J)       QUARTERDATA       A(28)                      13D . . . .
          SALESDATA         DS(34)                     10D . . . .
          SALESFORQTR(4)    S(7,2)                     19D . . . .


Some of you are undoubtedly wondering, "Isn't that an awful lot of typing?" Perhaps-if we had used SEU. But those who regularly read our columns know that we only use SEU when truly desperate, because there are better alternatives. When all you do is to highlight the first "Overlay(Quarter...)" and use Ctrl+C and Ctrl+V to copy and paste it into the required lines, it's not much of an issue. We like that this last version tends to be self-documenting. The fact that the fields are grouped together and the group is then redefined as an array should be helpful to those who come after you.


Before closing, we'll mention one more aspect of D-specs of which you may be unaware. Jons passionate dislike of compile-time data led him to use this technique as an alternative.

     D Messages        DS
     D                          20a   Inz('Invalid Item Code')
     D                          20a   Inz('Too many selections')
     D                          20a   Inz('Item Code required')
     D                          20a   Inz('Huh?')

     D    Msg                   20a   Overlay(Messages) Dim(4)

You may have noticed that the individual message fields have no field names. This is perfectly legal syntax, and it offers the advantage of placing all of the values that are to be used to initialize the array in the same spot as the array definition itself. There's no need to go to the other end of the source to locate the specific values for the elements-it's all right there.

In the following list, we've highlighted some of the lesser-known aspects of D-spec usage:

  1. Numeric and Date fields defined in the database retain their original format if defined in a DS. If the compiler allocates space for them (i.e., they arent in a DS) then all numeric fields are converted to packed format and all date fields are converted to *ISO or what ever Hspec DATFMT default is in place for the program.
  2. The OVERLAY keyword can be applied to the DS name. While this wasn't initially supported in RPG IV, its been in place for several releases now.
  3. If a field is defined externally in one of the files used by the program, no length or type specification is required to incorporate it into a DS.
  4. Fields in a DS dont need to be named.
  5. Group fields can be used in a DS and used to redefine the consituent fields or allow for block initialization.

This concludes our round-up of little-known D-spec features. If you have your own tips and techniques to add to our list, let us know.