In my quest to have a better understanding of how triggers worked I did quite a bit of searching on the web and reading different articles. Nick Litten had a wonderful starting point of one in RPG Free where he was unable to recall the original source of the code. This updated code was quite a bit easier to understand and follow. The original trigger template my previous employer had used was from the old AS400 magazine. The code worked, just not easy to follow.
I took his code and added some more meat and potatoes behind it.
With any triggers you will need to use ADDPFTRG to the physical if in non-Infor XA shop. In a Infor XA shop you will need to add the trigger in Trigger Program control maintenance in CAS.
I am using subroutines in this code, which might ruffle feathers of a few hard-core RPG Free programmers, but it does work. In my next article I will share on how to update a file to capture the trigger changes for audit purposes.
**FREE
Ctl-Opt
copyright(‘| Base Trigger Program V001 2026.March’)
debug option(*nodebugio: *srcstmt)
datfmt(iso-) timfmt(iso.)
indent(‘| ‘) ;
// ***
// F I L E D E C L A R A T I O N S
// ***
// ***
// *Note – update MYFILE to your actual file name you will be
// updating if any
// ***
Dcl-F MYFILE Usage(*input : *OUTPUT) Keyed ;
// ***
// *ENTRY PLIST – incoming parameters
// ***
Dcl-PI *n ;
TrgBuffer likeds(trigger) ;
TrgBufferLen int(10) ;
End-PI ;
// ***
// C O N S T A N T S
// ***
// ***
// Possible values for Event
// Insert (1) = trigger fires on new row/record creation
// Update (3) = Trigger fires on data modification.
// Delete (2) = Trigger fires on row/record removal.
// Read (4) = (IBM i specific)Trigger fires on read operation.
// ***
Dcl-C INSERT ‘1’ ;
Dcl-C DELETE ‘2’ ;
Dcl-C UPDATE ‘3’ ;
Dcl-C READ ‘4’ ;
// ***
// Possible values for Time
// After (1) = After the change or read operation
// Before (2) = Before the change operation
// (3) = Instead of the change operation
// ***
Dcl-C AFTER ‘1’ ;
Dcl-C BEFORE ‘2 ‘ ;
// ***
// Possible values for Commitlocklev
// Commit lock Level
// *ALL
// All records accessed (read, changed, added, or
// deleted) are locked until a COMMIT or ROLLBACK is
// performed. Other programs cannot read these locked
// records.
// *CS = Cursor Stability
// All records accessed (read, changed, added, or
// deleted) are locked until a COMMIT or ROLLBACK is
// performed. Other programs cannot read these locked
// records.
// *CHG (Low Lock Level / Read Stability)
// Only records read for update, or those that are
// changed, added, or deleted, are locked until a
// COMMIT or ROLLBACK. Records read without an
// update intent may be read by other jobs.
// *NONE (No Commitment Control)
// Changes are made to the database immediately
// and cannot be rolled back. No locks related to
// commitment control are acquired.
// ***
Dcl-C CMTNONE ‘0’ ;
Dcl-C CMTCHANGE ‘1’ ;
Dcl-C CMTCS ‘2’ ;
Dcl-C CMTALL ‘3’ ;
// ***
// S T A N D A L O N E F I E L D S
// ***
Dcl-S ProgName char(10)Inz(‘Testing’) ;
Dcl-S CurTime Time ;
Dcl-S CurDate Date ;
Dcl-S BefRecPtr pointer ;
Dcl-S AftRecPtr pointer ;
// ***
// D A T A S T R U C T U R E S
// ***
// ***
// Trigger buffer information
// ***
Dcl-DS trigger qualified ;
File char(10) ;
Library char(10) ;
Member char(10) ;
Event char(1) ;
Time char(1) ;
CommitLockLev char(1) ;
*n char(3) ;
Ccsid int(10) ;
Rrn int(10) ;
*n char(4) ;
BefRecOffset int(10) ;
BefRecLen int(10) ;
BefNullOffset int(10) ;
BefNullLen int(10) ;
AftRecOffset int(10) ;
AftRecLen int(10) ;
AftNullOffset int(10) ;
AftNullLen int(10) ;
End-ds ;
// ***
// “Before” record image
// *Note – update myfile to your actual file name
// ***
Dcl-DS Old extname(‘MYFILE’) based(BefRecPtr) qualified end-ds ;
// ***
// “After” record image
// *Note – update myfile to your actual file name
// ***
Dcl-DS New extname(‘MYFILE’) based(AftRecPtr) qualified end-ds ;
// ***
// Map trigger buffer into data structures
// ***
AftRecPtr = %ADDR(TrgBuffer) + TrgBuffer.AftRecOffset ;
BefRecPtr = %ADDR(TrgBuffer) + TrgBuffer.BefRecOffset ;
// ***
// P R O G R A M L O G I C
// ***
// ***
// Trigger processing goes here
// Refer to Old field names with “old.” qualification
// Refer to new field names with “new.” qualification
// ***
Select ;
// *******************************************************************// --------- Insert / Add - Before// *******************************************************************
when TrgBuffer.event = INSERT and TrgBuffer.time = BEFORE ;
// add some logic here for Insert processing// for example, something like this, where name was a field name in the file:// If old.name <> new.name ;// write some report detailing customers changed of name// endif;Exsr $AddBefore ;// *******************************************************************// --------- Insert / Add - After// *******************************************************************
when TrgBuffer.event = INSERT and TrgBuffer.time = AFTER ;
// add some logic here for Insert processing// for example, something like this, where name was a field name in the file:// If old.name <> new.name ;// write some report detailing customers changed of name// endif;Exsr $AddAfter ;// *******************************************************************// --------- Delete - BEFORE// *******************************************************************
when TrgBuffer.event = DELETE and TrgBuffer.time = BEFORE ;
// add some logic here for Delete processing or insert into subroutineExsr $DeleteBefore ;// *******************************************************************// --------- Delete - AFTER// *******************************************************************
when TrgBuffer.event = DELETE and TrgBuffer.time = AFTER ;
// add some logic here for Delete processing or insert into subroutineExsr $DeleteAfter ;// *******************************************************************// --------- Update / Change - AFTER// *******************************************************************
when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;
// add some logic here for Update processing or insert into subroutineExsr $ChangeAfter ;// *******************************************************************// --------- Update / Change - BEFORE// *******************************************************************
when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;
// add some logic here for Update processing or insert into subroutineExsr $ChangeBefore ;// *******************************************************************// --------- Update / Change - AFTER// *******************************************************************
when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;
// add some logic here for Update processing or insert into subroutineExsr $ChangeAfter ;// *******************************************************************// --------- Read only - Generally not used// *******************************************************************
when TrgBuffer.event = READ ;
// add some logic here for Read processing or insert into subroutineExsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
// add some logic here for Read processing or insert into subroutine
Exsr $Read ;
Leave a Reply