The trigger program is added to the file via a CL command in the program DB008C shown in Listing 6.5.
PGM
/*----------------------------------------------------------------------*/
/* Add trigger DB008R to file Customer to perform data */
/* validation.
/*----------------------------------------------------------------------*/
ADDPFTRG FILE(CUSTOMER) TRGTIME(*BEFORE)+
TRGEVENT(*INSERT) PGM(DB008R) ALWREPCHG(*NO)
ENDPGM
ADDPFTRG FILE(CUSTOMER) TRGTIME(*BEFORE) +
TRGEVENT(*UPDATE) PGM(DB008R) ALWREPCHG(*NO)
ENDPGM
The trigger program DB008R is shown in Listing 6.6.
FStates CT F 2 Disk
DStateCodes S 2A Dim(100) Perrcd(1) Fromfile(States)
*----------------------------------------------------------------------
* I'm using pointers instead of SUBSTR because %Addr
* retains packed data. Substr doesn't handle embedded
* packed data fields.
*----------------------------------------------------------------------
*
DpBefore S *
DpAfter S *
*
DBefore E DS ExtName(CUSTOMER) Prefix(B_)
D Based(pBefore)
DAfter E DS ExtName(CUSTOMER) Prefix(A_)
D Based(pAfter)
D*
DX S 3 0
D*
/*-----------------------------------------------------------------*/
* Trigger Buffer and Trigger Buffer Length Declarations
/*-----------------------------------------------------------------*/
DBufferLen S 10I 0
DTrigBuff DS
D TrigFile 10A
D TrigLib 10A
D TrigMbr 10A
D TrigEvent 1A
D TrigTime 1A
D TrigCommit 1A
D TrigRes1 3A
D TrigCCSID 10I 0
D TrigRRN 10I 0
D TrigRes2 4A
D TrigB4OS 10I 0
D TrigB4Len 10I 0
D TrigB4NBM 10I 0
D TrigB4NBL 10I 0
D TrigAftOS 10I 0
D TrigAftLen 10I 0
D TrigAfNBM 10I 0
D TrigAfNBL 10I 0
*-----------------------------------------------------------------
* Trigger Constants
*-----------------------------------------------------------------
D@Insert C '1'
D@Delete C '2'
D@Update C '3'
D@Before C '2'
D@After C '1'
*-----------------------------------------------------------------
* API Declarations
*-----------------------------------------------------------------
DSMMsgId S 7 Inz('CPF9898')
DSMMsgFile S 20 Inz('QCPFMSG *LIBL')
DSMMsgTxt S 100
DSMMsgLen S 10I 0 Inz(%Size(SMMsgTxt))
DSMMsgType S 10 Inz('*ESCAPE')
DSMMsgQ S 10 Inz('*')
DSMMStack# S 10I 0 Inz(1)
DSMMsgKey S 4
*
DAPIErrorDS DS
D APIBytes 10I 0 Inz(%Size(APIErrorDS))
D APIBytesOut 10I 0
D APIErrID 7A
D APIReserved 1A
D APIErInDta 256A
*-----------------------------------------------------------------
* Error Message Constants
*-----------------------------------------------------------------
D@Error1 C 'Customer Number cannot be blank'
D@Error2 C 'Company Name cannot be blank'
D@Error3 C 'Contact Name cannot be blank'
D@Error4 C 'Address cannot be blank'
D@Error5 C 'City cannot be blank'
D@Error6 C 'Zip Code cannot be blank'
D@Error7 C 'State cannot be blank'
D@Error8 C 'Invalid State Entered'
*-----------------------------------------------------------------
* Input parameters are passed automatically when the trigger
* fires. Passed are the trigger buffer and trigger buffer length.
*-----------------------------------------------------------------
C *Entry PList
C Parm TrigBuff
C Parm BufferLen
*-----------------------------------------------------------------
* Map the data structures for the before and after images to
* the offset location in the trigger buffer using pointers.
*-----------------------------------------------------------------
C Eval pBefore = %Addr(TrigBuff) + TrigB40S
C Eval pAfter = %Addr(TrigBuff) + TrigAft0S
*
C If TrigEvent = @Update Or
TrigEvent = @Insert
*-----------------------------------------------------------------
* Validate entire record. Send an error message for each
* violation found.
*-----------------------------------------------------------------
C If A_CMCUST# = *Blanks
C Movel(p) @Error1 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMCOMP = *Blanks
C Movel(p) @Error2 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMCONT = *Blanks
C Movel(p) @Error3 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMADD1 = *Blanks
C Movel(p) @Error4 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMCITY = *Blanks
C Movel(p) @Error5 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMZIP = *Blanks
C Movel(p) @Error6 SMMsgTxt
C Exsr SendError
C Endif
*
C If A_CMST = *Blanks
C Movel(p) @Error7 SMMsgTxt
C Exsr SendError
C Else
C Eval X = 1
C A_CMST Lookup StateCodes(X) 90
C If Not(*In90)
C Movel(p) @Error8 SMMsgTxt
C Exsr SendError
C Endif
C Endif
*
C Endif
*
C Return
/*-----------------------------------------------------------------*/
* If any exceptions were found - Use QMHSNDPM API to send
* a hard error back.
/*-----------------------------------------------------------------*/
C SendError Begsr
C*
C Call 'QMHSNDPM'
C Parm SMMsgId
C Parm SMMsgFile
C Parm SMMsgTxt
C Parm SMMsgLen
C Parm SMMsgType
C Parm SMMsgQ
C Parm SMMStack#
C Parm SMMsgKey
C Parm APIErrorDS
C*
C Endsr
The example program DB008C uses the ADDPFTRG command to add the trigger program DB008R to the CUSTOMER file. The trigger is fired every time a record in the CUSTOMER file is added or changed. Because the trigger monitors both insert and update events, the trigger is added twice. The program checks and validates the input data, returning an error message if the data does not pass.
When executed, the ADDPFTRG command listed in program DB008C adds trigger program DB008R to the CUSTOMER file. The ADDPFTRG command uses the following parameters.
The Physical File (FILE) Parameter The FILE parameter is required and specifies the qualified name of the file to which the trigger is to be added.
The Trigger Time (TRGTIME) Parameter The TRGTIME parameter is required and specifies the timing of the firing of the trigger program. The permissible values for the TRGTIME parameter are:
______________________________________________________________________________
*BEFORE The trigger executes before the record is processed
*AFTER The trigger executes after the record is processed
______________________________________________________________________________
The example uses the value *BEFORE, meaning the trigger runs before the employee record changes or additions are written to the file. This allows you to reject records that do not meet the validation criteria.
The Trigger Event (TRGEVENT) Parameter The TRGEVENT parameter is required and defines the file event that causes the trigger program to fire. The permissible values for the TRGEVENT parameter are:
______________________________________________________________________________
*INSERT The trigger executes when a new record is added
*UPDATE The trigger executes when a record is updated
*DELETE The trigger executes when a record is deleted
______________________________________________________________________________
The example adds two triggers, one having the value *UPDATE, meaning the trigger runs before an application's record changes are written. The second trigger has a value of *INSERT, meaning the trigger runs before new records are written to the CUSTOMER file.
The Program Name (PGM) Parameter The PGM parameter is required and specifies the qualified name of the trigger program to be added.
The Replace Trigger (RPLTRG) Parameter The RPLTRG parameter specifies whether the new trigger is to replace any existing trigger defined for the file, time, and event combination. The permissible values for the RPLTRG parameter are:
______________________________________________________________________________
*YES The new trigger will replace an existing trigger
*NO The new trigger will not replace an existing trigger
______________________________________________________________________________
The example uses the value *NO.
The Allow Repeated Change (ALWREPCHG) Parameter The ALWREPCHG parameter specifies whether the new trigger has the capability to change the original record that caused the trigger to fire. The permissible values for the ALWREPCHG parameter are:
______________________________________________________________________________
*YES The trigger program can alter the original record
*NO The trigger program cannot alter the original record
______________________________________________________________________________
The example uses the value *NO, restricting it from changing any of the values in the Customer record.
The Trigger Update Condition (TRGUPDCND) Parameter The TRGUPDCND is used only for triggers having a trigger event of *UPDATE. The parameter specifies if the trigger is to be fired only when a field is actually changed by an update operation. The permissible values for the TRGUPDCND parameter are:
______________________________________________________________________________
*CHANGE The trigger fires only if fields have changed
*ALWAYS The trigger fires on all update operations
______________________________________________________________________________
The example uses the value *CHANGE, specifying that one or more field values must actually be changed to fire the trigger.
When executed, the trigger program DB008R validates the data being added or changed in the CUSTOMER file. If any field does not meet the validation requirements, the program sends an escape error message to the program message queue. This results in a hard error for the application that initiated the add or change. This program can be used as an example of how to use a trigger program to perform data validation and of how to employ the Send Program Message API.
The code that is common to all trigger programs defines the layout of the trigger buffer parameter. A simple coding technique employing pointers defines the trigger buffer in a flexible and reliable way. If you have not used pointers in an RPG program before, they are simply a way to address a specific storage location. As discussed earlier in this chapter, the trigger buffer parameter passed to your program varies in length because the length of the trigger record varies from file to file. Pointers allow you to soft code the location of the record images with the trigger buffer using the offset values also found in the parameter. Figure 6.15 shows the definition of the pointers and data structures that define the input parameters.
The input parameter TRIGBUFF is defined as a data structure, with subfields parsing out each individual field within the fixed-length portion of the buffer. The offset values, provided in the subfields TRIGB4OS and TRIGAFTOS, tell your program the starting positions within the buffer for the before and after record images.
To avoid the hard coding of the subfields of the customer record within both the before and after images, the file is used to create two external data structures called BEFORE and AFTER. The data structure definitions use the DTAARA keyword to base the layout of the structure on the Customer file. The PREFIX keyword is used on each to add a unique prefix of either ‘B_’ or ‘A_’ to the field names. The BASED keyword specifies that the data structure is to be loaded with the data residing in the address provided by the pointers PBEFORE and PAFTER.
The before and after record images are loaded into the data structures by setting the pointers to the address locations of the records within the trigger buffer. This is performed in Figure 6.16 using the RPG Address-Of function. The appropriate offset is added to the address of the buffer and is loaded into the pointer variable. Because the data structures BEFORE and AFTER are based on the pointer variables, they reflect the proper values.
Employing pointers to define the record images in the trigger buffer is a flexible, soft-coded approach to use for all your trigger programs. Figure 6.17 provides a further conceptual look at the technique.
Once the trigger buffer has been defined, allowing easy access to the before and after record images, the main program logic validates each data field. Figure 6.18 shows the validation code.
The main trigger logic begins by verifying that the trigger event is either a record update or an add. If it is an add or update, the code tests the after-change value of each data field. If any field does not meet the desired requirements, an error message is sent by executing the subroutine SENDERROR. The subroutine SENDERROR is shown in Figure 6.19.
To send a message to a program message queue, the RPG program could call a CL program that would use a SNDPGMMSG command to send the desired error. However, it is just as easy and more flexible to employ a system API to do the same thing. For more detailed explanations of API use, please refer to chapter 4.
The system API QMHSNDPM sends a message to a program message queue. The API parameters are defined in the program definition specifications and provide details used to instruct message delivery when the API is called. Figure 6.20 shows the definition of the API parameters.
When you create your trigger programs in ILE, you need to decide how it will fit into your overall ILE strategy. What is the appropriate activation group the trigger should run under? It really depends on what type of applications that will be calling it. It is generally appropriate to create your trigger programs to run under a specific named activation group.