DL/I examples
- Searching within a parent segment
- Searching with another non-key field
- Searching based on information in another record
- Searching for a non-root segment
- Searching with a secondary index
- Using path calls to access multiple segments
- Reading all segments with a single I/O statement
- Using dynamic arrays
Searching within a parent segment
The default get next statement starts at the current position in the database and searches through the entire database for the next occurrence of the target segment under any parent. To limit the scope of the search to the currently established dependent chain, use the get next inParent statement.
get myOrder with #dli {
GU STSCCST (STQCCNO = :myCustomer.customerNo)
STSCLOC (STQCLNO = :myLocation.locationNo)
STSOCORD (STQCODN = :myOrder.orderDateno) };get next inParent myItem;
while (myItem not noRecordFound)
// process the current item
get next inParent myItem;
endGNP STLCITM Searching with another non-key field
- You want to search on the creditBalance field (STFCSBL) in the credit segment (STSCSTA). To do this, define a variable of type decimal(12,2) (for example, "targetBalance") that contains the specified amount that you want to search for. The definition for targetBalance should match the definition for the creditBalance field in the credit segment.
- Write a get next statement for the myCrStatus record.
- Add a #dli directive to the line, modifying the default code. Add a qualified SSA that looks for a segment where the amount in the creditBalance field is greater than targetBalance.
- Include a path command code (*D) to retrieve the customer segment (STSCCST) that corresponds to the credit segment.
targetBalance decimal(12,2);
targetBalance = 10,000.00;
get next myCustomer,myCrStatus with #dli{
GU STSCCST*D STSCSTA (STFCSBL >= :targetBalance) };Searching based on information in another record
Record CustomerData type basicRecord
10 customerNo char(6)
...
end
Program myProgram
(customerParm CustomerData)
{ @DLI{ psb = "myCustomerPSB" }}
//declare variables
myCustomerPSB CustomerPSBRecordPart;
myCustomer CustomerRecordPart;customerParm.customerNo.
There are three ways to code this as follows:- Assign the value of
customerParm.customerNotomyCustomer.customerNoand then use implicit DL/I I/O as follows:myCustomer.customerNo = CustomerParm.customerNo; get myCustomer;In this case, EGL creates the normal default SSAs, resulting in the following pseudo-DL/I code:
The advantage of this technique is that it is very simple. The disadvantage is the very slight performance overhead of moving the customer number to the segment record.GU STSCCST (STQCCNO = :myCustomer.customerNo) - Use the #dli directive and explicit DL/I I/O so that you can use
the customerParm.customerNo field as the host variable as follows:
This technique avoids the performance overhead of moving the customer number. However, it does take slightly longer to paste in the default #dli directive and then modify the code to use the correct record variable name for the parameter record.get myCustomer with #dli { GU STSCCST (STQCCNO = :customerParm.customerNo) } ; - Add the hostVarQualifier property in
the DLISegment record to specify the qualifier for the host variable
as follows:
Record CustomerRecordPart type DLISegment {segmentName="STSCCST", keyItem="customerNo", hostVarQualifier="customerParm" } 10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field ... endThen you can use implicit DL/I I/O without having to first assigncustomerParm.customerNotomyCustomer.customerNoas follows:get myCustomer;The pseudo-DL/I code that EGL creates in this case is:GU STSCCST (STQCCNO = :customerParm.customerNo)The advantage of this technique is that it results in the same pseudo-DL/I code that you coded for the #dli directive, without you actually having to code the #dli directive. The disadvantage is that EGL now uses
customerParmas the qualifier for every implicit DL/I database I/O statement that uses the CustomerRecordPart.
Searching for a non-root segment
You can use implicit DL/I database I/O to retrieve a non-root segment. EGL creates the entire chain of SSAs for you based on the hierarchical position of the target segment in the PCB. However, for the higher level segments in the hierarchy, EGL cannot automatically determine the name of the program variable to use as the qualifier for the segment.
myCustomer CustomerRecordPart;
myLocation LocationRecordPart;get myLocation;EGL
will create the following pseudo-DL/I code: GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo)
STSCLOC (STQCLNO = :myLocation.locationNo)Notice that while EGL correctly associated the variable myLocation with
the segment STSCLOC, EGL was unable to find the variable myCustomer that
is based on the CustomerRecordPart. EGL only knows that myLocation is
of type LocationRecordPart, whose parent segment is CustomerRecordPart,
and that the keyItem for CustomerRecordPart
is customerNo.
- You can name your record variables with the same name as the record
parts on which they are based. For example, change the variable declaration
for myCustomer to CustomerRecordPart.
EGL creates the same pseudo-DL/I code:CustomerRecordPart CustomerRecordPart;GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo)Because EGL creates the default SSAs from the PCB hierarchy information, this is an easy way of ensuring that the variable names EGL uses match your program's record variable declarations. The disadvantage is that this technique does not follow the general practice of using different names for parts and variables.
- Use the #dli directive and explicit DL/I database I/O as follows:
get myLocation with #dli { GU STSCCST (STQCCNO = :myCustomer.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo)The advantage of this technique is that it is very clear which host variables are being used. It is an easy technique to use if the host variable qualifier or field name is different from the record variable that is based on the DL/I segment record. The disadvantage is the same as for any explicit I/O -- if the hierarchy or the key fields change, the explicit DL/I database I/O statement does not change automatically.
- Modify your definition for CustomerRecordPart to include the hostVarQualifier property
as follows:
Now if you use the following get statement:Record CustomerRecordPart type DLISegment { segmentName="STSCCST", keyItem="customerNo", hostVarQualifier = "myCustomer" } 10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field ... end
EGL will produce the correct pseudo-DL/I code:get myLocation;GU STSCCST (STQCCNO = :myCustomer.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo)The advantage of this technique is that you can use implicit DL/I database I/O and have different names for the record variables and the DL/I segment records. The disadvantage is that EGL now uses
myCustomeras the qualifier for every implicit DL/I database I/O statement that uses the CustomerRecordPart.
Searching with a secondary index
customerName is
the secondary index, then you can add an additional PCB record to
the PSB Record part as follows:// database PCB for customer by Name
customerNamePCB DB_PCBRecord
{ @PCB { pcbType = DB, pcbName = "STDCNAM",
secondaryIndex = "STUCCNM",
hierarchy = [ @relationship { segmentRecord = "CustomerRecordPart" },
... ] }
}The pcbName property must match an actual DL/I database PCB in the runtime PSB. The secondaryIndex property must provide the same field name as your database administrator specifies in the XDFLD statement of the runtime PCB. There are now two PCB records in the PSB record that include the CustomerRecordPart in their hierarchy.
myCustomer CustomerRecordPart;
get myCustomer;EGL will create the pseudo-DL/I code based
on the first PCB record in the PSB record that includes the CustomerRecord
part.get myCustomer usingPCB customerNamePCB;EGL
will create the following pseudo-DL/I code:GU STSCCST (STUCCNM = :myCustomer.customerName)- The DL/I name of the segment (property
segmentName = "STSCCST"). - The DL/I name of the secondary index field (property
secondaryIndex = "STUCCNM"). - The EGL field name for the comparison value (property
dliFieldName = "STUCCNM"and then the corresponding field name customerName).
// orders view of customer database
ordersByReferencePCB DB_PCBRecord
{ @PCB { pcbType = DB, pcbName = "STDCDBL",
secondaryIndex = "STFCORF", //use DL/I name
hierarchy = [
@relationship { segmentRecord = "OrderRecordPart" },
@relationship { segmentRecord = "LocationRecordPart",
parentRecord = "OrderRecordPart" },
@relationship { segmentRecord = "CustomerRecordPart",
parentRecord = "LocationRecordPart" },
@relationship { segmentRecord = "ItemRecordPart",
parentRecord = "OrderRecordPart" }
]}
};
end get myOrder, myCustomer with #dli{
GU STPCORD*D (STQCODN = :myOrder.orderReference)
STSCLOC
STSCCST };Using path calls to access multiple segments
get myCustomer, myLocation, myOrder;This
statement generates the following DL/I pseudocode:GU STSCCST*D (STQCCNO = :myCustomer.customerNo)
STSCLOC*D (STQCLNO = :myLocation.locationNo)
STPCORD (STQCDDN = :myOrder.orderDateNo)get myCustomer, myLocation, myOrder forUpdate;
replace myOrder with #dli{
REPL STSCCST*N
STSCLOC*N
STPCORD };The default DL/I call EGL builds for
a delete function that follows a get
forUpdate statement with D command codes does not delete
each segment retrieved. It deletes only the target segment specified
on the delete statement.Reading all segments with a single I/O statement
- Write a get next statement for the record that represents the largest segment in the database. This ensures that any segment you read will not exceed allocated memory.
- Add the default DL/I call to your code. Edit the #dli directive to delete the single SSA.
- Create records matching the other segments in the database. Declare the records in the program and specify that each of the records redefines the record used in step 1 above so that all the records occupy the same area of memory.
- Check dliVar.segmentName after the get next statement to determine the type of segment that was retrieved.
- Access the retrieved segment from the corresponding record structure.
myHistory HistoryRecordPart
redefCustomer CustomerRecordPart {redefines=myHistory};
redefLocation LocationRecordPart {redefines=myHistory};
...
//read next segment, whatever type it is, into history record
while (myHistory not EOF)
get next myHistory with #dli{
GN };
//so what type was it?
case (dliVar.segmentName)
when "STSCCST" // it was a customer
printCustomer();
when "STSCLOC" // it was a location
printLocation();
...
end
end
Using dynamic arrays
You can use the get and get next statements to retrieve DL/I segments to a dynamic array. Because there is no key field for the array itself (only for members of the array), you must use some special techniques so that EGL will create the correct code.
myCustomer CustomerRecordPart;
myLocation LocationRecordPart;
myOrder OrderRecordPart;
myOrderArray OrderRecordPart [] {maxsize = 20}; // array of ordersmyCustomer.customerNo = "123456";
myLocation.locationNo = "ABCDEF";
myOrderDateNo = "20050730A003";
get myOrderArray; // fill the array the first time
... do some processing
get next myOrderArray; // get the next batch of 20 ordersget myOrderArray with #dli{
GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo)
STSCLOC (STQCLNO = :LocationRecordPart.locationNo)
STPCORD (STQCODN = :OrderRecordPart.orderDateNo)
GN STPCORD };get next myOrderArray with #dli{
GN STPCORD };Filling the dynamic array for the first batch of 20 orders with a get statement requires an initial GU call followed by a loop of GN calls until the array is full or DL/I runs out of segment occurrences. In the pseudo-DL/I code that EGL creates, the GU call retrieves the first order segment. EGL treats the GN call as a loop and provides the logic to loop until the array is full or DL/I runs out of segment occurrences. Similarly, EGL treats the get next statement as a loop and provides the loop control logic for you.
- Name your DL/I segment records with the same name as the segments in the DL/I database. Also change your record variable names to be the same name as the segments in the DL/I database. For example, change the customer record part definition from the following:
to the following:Record CustomerRecordPart type DLISegment { segmentName="STSCCST", keyItem="customerNo" } 10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field ... endRecord STSCCST type DLISegment { segmentName="STSCCST", keyItem="customerNo" } 10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field ... endMake similar changes for the location segment (STSCLOC) and the order segment (STPCORD). Then change your PSB record to use the new names as follows:Record CustomerPSBRecordPart type PSBRecord { defaultPSBName="STBICLG" } // database PCB customerPCB DB_PCBRecord { @PCB { pcbType = DB, pcbName = "STDCDBL", hierarchy = [ @relationship { segmentRecord = "STSCCST" }, @relationship {segmentRecord="STSCLOC", parentRecord="STSCCST"}, @relationship {segmentRecord="STPCORD", parentRecord="STSCLOC"} ]}}; endChange the declaration of your record variables and myOrderArray so that they reference the new DL/I segment record parts as follows:STSCCST STSCCST; STSCLOC STSCLOC; STPCORD STPCORD; myOrderArray STPCORD [] {maxsize = 20}; // array of ordersNow when you issue the following get statement:
EGL creates the following pseudo-DL/I code for the get statement and the host variable qualifiers use the correct record variable names:get myOrderArray; // fill the array the first timeget myOrderArray with #dli{ GU STSCCST (STQCCNO = :STSCCST.customerNo) STSCLOC (STQCLNO = :STSCLOC.locationNo) STPCORD (STQCODN = :STPCORD.orderDateNo) GN STPCORD };Because EGL creates the default SSAs from the PCB hierarchy information, this is an easy way of ensuring that the variable names EGL uses match your program's record variable declarations. The disadvantage is that this technique does not follow the general practice of using different names for parts and variables.
- Use the #dli directive and explicit DL/I database I/O as follows:
get myOrderArray with #dli{ GU STSCCST (STQCCNO = :myCustomer.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo) STPCORD (STQCODN = :myOrder.orderDateNo) GN STPCORD };The advantage of this technique is that it is very clear which host variables are being used. This technique is particularly useful if you must change the default DL/I code -- for example if you do not know the first order number for the customer and want to use the following DL/I code:myOrder.orderDateNo = ""; get myOrderArray with #dli{ GU STSCCST (STQCCNO = :myCustomer.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo) STPCORD (STQCODN >= :myOrder.orderDateNo) // using >= instead of = GN STPCORD };The disadvantage of this technique is the same as for any explicit I/O -- if the hierarchy or the key fields change, the explicit DL/I database I/O statement does not change automatically.
- Modify the definition for each of your DL/I segment records to include the hostVarQualifier property with the name of your program record variable. For example, change the CustomerRecordPart to the following:
Record CustomerRecordPart type DLISegment { segmentName="STSCCST", keyItem="customerNo", hostVarQualifier = "myCustomer" } 10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field ... endNow if you use the following record declarations and get statement:myCustomer CustomerRecodPart; myLocation LocationRecordPart; myOrder OrderRecordPart; myOrderArray OrderRecordPart [] {maxsize = 20}; // array of orders get myOrderArray; // fill the array the first timeEGL will produce the correct pseudo-DL/I code:get myOrderArray with #dli{ GU STSCCST (STQCCNO = :myCustomer.customerNo) STSCLOC (STQCLNO = :myLocation.locationNo) STPCORD (STQCODN = :myOrder.orderDateNo) GN STPCORD };The advantage of this technique is that you can use implicit DL/I database I/O and have different names for the record variables and the DL/I segment records. The disadvantage is that EGL now uses myCustomer as the qualifier for every implicit DL/I database I/O statement that uses the CustomerRecordPart, LocationRecordPart, and OrderRecordPart.