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 subroutine
Exsr $DeleteBefore ;
// *******************************************************************
// --------- Delete - AFTER
// *******************************************************************

when TrgBuffer.event = DELETE and TrgBuffer.time = AFTER ;

// add some logic here for Delete processing or insert into subroutine
Exsr $DeleteAfter ;
// *******************************************************************
// --------- Update / Change - AFTER
// *******************************************************************

when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;

// add some logic here for Update processing or insert into subroutine
Exsr $ChangeAfter ;
// *******************************************************************
// --------- Update / Change - BEFORE
// *******************************************************************

when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;

// add some logic here for Update processing or insert into subroutine
Exsr $ChangeBefore ;
// *******************************************************************
// --------- Update / Change - AFTER
// *******************************************************************

when TrgBuffer.event = UPDATE and TrgBuffer.time = BEFORE ;

// add some logic here for Update processing or insert into subroutine
Exsr $ChangeAfter ;
// *******************************************************************
// --------- Read only - Generally not used
// *******************************************************************

when TrgBuffer.event = 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 ;

// add some logic here for Read processing or insert into subroutine

Exsr $Read ;

Posted in

Leave a Reply

Discover more from Candy Hein Consulting

Subscribe now to keep reading and get access to the full archive.

Continue reading