READ トリガー/RPG でトリガー処理

トリガーというのはRDB の一般的な機能のひとつで、データベーステーブルに UPDATE/INSERT/DELETE などの更新があった時に自動的に事前定義しておいたスクリプトを発行することができる、という機能です。

発行されるタイミングは更新の前(Before)/後(After)が指定でき、更新前であれば何らかのチェック、更新後であればロギングなどの用途に使われることが多いようです。(ちなみに Microsoft SQL Server には Before トリガーはありません。After のみです。また、SQL Server には、Oracle にも DB2/400 にも存在する行レベルトリガーとステートメントレベルトリガーの区別もありません。そんなふうにデータベースエンジンによって多少異なっています)

通常、処理はSQL で記述されます。Oracle の場合はPL/SQL、SQL Server の場合はTransact-SQL、DB2/400 の場合はDB2 ファミリーのSQL PL(Procedure Language) といった具合です。ちなみに SQL PL はANSI で定められたSQL/PSM (Persistent Stored Modules) とほぼ同じものになります。
SQL トリガーについては、このサイトでも「SQL トリガーの作成」や「SQL トリガーの起動タイミング (Each Row/Each Statement)」、「SQL トリガーのモード (DB2SQL/DB2ROW)」、「INSTEAD OF トリガーとカラムの暗号化」などで取り上げてきました。

DB2/400 には SQL で記述されるトリガーの他に、RPG や COBOL、C などで書いたプログラムをトリガー処理プログラムとして登録できる、という機能があります。つまり、SQL では記述しにくい細かな処理や、それどころか SQL では記述しようのないデータベース関連以外の処理なども、トリガーによって起動される処理として定義することができます。

また、もうひとつさらに、これは他のデータベースでは見たことがないのですが、毎 Row の Read が行われたタイミングでトリガーを起動させることができます。つまり READ トリガー、ということになります。

これは行単位に何を読み取ったのかがわかりますから監査目的にも使用できますし、ある特別な行だけをある特定の人にしか見せないといったようなセキュリティ目的にも使用できます。

CPYF だろうが DSPPFM だろうが、レコード内容を読み取った時点でトリガー処理が行われます。ですから、原理的に毎行に対して行われる AFTER トリガーということになりますね。
ちなみに、「データベースのオープン毎に処理を行う (Exit プログラム)」で紹介した方法では、SQL や Query には反応しますが DSPPFM には反応しません。

ただし、原理的に、毎行実行されてしまいますので、ブロッキングや非同期バッファリングなど、OS が読み込みのパフォーマンスを上げるために行っている処理の大半が効かなくなってしまいます。パフォーマンスの問題が生じますので、考慮が必要です。たとえば、プログラム内で使用する場合、バッチで大量の読み込みが発生するような場合は、後述する CHGPFTRG コマンドで一時的に無効にしてから実行する、などの工夫が必要になるでしょう。


トリガーの追加

プログラムを指定するトリガーは ADDPFTRG コマンドで追加します。SQL の ALTER TABLE コマンドでは行えません。

トリガー名は*GEN のままなので、システムが生成したトリガー名がついています。

トリガーの有効/無効

トリガーを有効にしたり無効にしたりすることが、CHGPFTRG コマンドでできます。登録されているトリガー単位でできるのですが、その場合はトリガー名を指定することになります。トリガー名を*GEN でシステムに生成させるとかなり使いにくい名前になるので、覚えやすい名前にしてちゃんと名前を指定しておいた方がいいでしょう。

トリガーの除去

RMVPFTRG コマンドでテーブルとトリガーの結びつきを開放することができます。トリガープログラムが削除されたりしてしまうわけではありません。

トリガーの定義

先ほど書いたように、トリガー名を指定することもできます。ファイル/テーブル内で固有であればOK です。

DSPFD コマンドでファイル記述を見てみると、ちゃんと指定された名前になっていますね。

トリガーの無効化

トリガー名を指定して、個別にトリガーの状態を変更させることができます。
こういう時にはやはり *GEN ではなく、名前をつけておいた方がなにかと便利です。

いったん無効にして、

大量のレコード/行の挿入などトリガーが存在するとパフォーマンスに多大な影響のあるような処理を行って、終了後に戻す、といったようなことが割合一般的に行われています。

トリガー処理プログラムの作成

トリガー処理プログラムには、トリガーバッファとそのバッファの長さが渡されます。

     D Trg_Simple      PR                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          
      *                                                                                             
     D Trg_Simple      PI                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          

トリガーバッファの内容は以下のようなものです。

     D TrgInfo         DS                  Qualified                                                
     D  File                         10a                                                            
     D  Library                      10a                                                            
     D  Member                       10a                                                            
     D  Event                         1a                                                            
     D  Time                          1a                                                            
     D  CmtLckLvl                     1a                                                            
     D                                3                                                             
     D  CCSID                        10i 0                                                          
     D  RRN                          10i 0                                                          
     D                                4                                                             
     D  BfrRecOfs                    10i 0                                                          
     D  BfrRecLen                    10i 0                                                          
     D  BfrNullOfs                   10i 0                                                          
     D  BfrNullLen                   10i 0                                                          
     D  AftRecOfs                    10i 0                                                          
     D  AftRecLen                    10i 0                                                          
     D  AftNullOfs                   10i 0                                                          
     D  AftNullLen                   10i 0                                                          

トリガーの起動イベントの種類とタイミングによりますが、更新前/更新後のレコードのイメージを取得することができます。
実際に QCUSTCDT テーブルを使用するのではなくコピーした別のファイルを使うのですが、レコード様式が一緒なのでそのまま ExtName(QCUSTCDT) と指定してしまっています。コンパイルする時にこのファイルがライブラリーリスト上にないとエラーになります。コンパイル時には、ライブラリー QIWS をライブラリーリストに入れておいてください。ちなみにコンパイルに特別な考慮点はありません。ごく普通にして OK です。

     D  BfrRecPtr      S               *                                                            
     D  Original     E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(BfrRecPtr)                                         
     D                                     Qualified                                                
      *                                                                                             
     D  AftRecPtr      S               *                                                            
     D  New          E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(AftRecPtr)                                         
     D                                     Qualified
      *                                                                                             
      /Free                                                                                         
              BfrRecPtr = %addr(TrgBuffer) + TrgBuffer.BfrRecOfs;                                   
              AftRecPtr = %addr(TrgBuffer) + TrgBuffer.AftRecOfs;

以下のプログラムでは、トリガーバッファ内から READ されたファイルの名前とそのレコードの RRN とを、ジョブの実行ユーザーをプログラム状況データ構造から拾って、あわせて表示するようにしています。

     H DFTACTGRP(*no)                                                                               
      *                                                                                             
     D                SDS                                                                           
     D  dsUser               254    263a                                                            
     D  crUser               358    367a                                                            
      *                                                                                             
     D Trg_Simple      PR                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          
      *                                                                                             
     D Trg_Simple      PI                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          
      *                                                                                             
     D TrgInfo         DS                  Qualified                                                
     D  File                         10a                                                            
     D  Library                      10a                                                            
     D  Member                       10a                                                            
     D  Event                         1a                                                            
     D  Time                          1a                                                            
     D  CmtLckLvl                     1a                                                            
     D                                3                                                             
     D  CCSID                        10i 0                                                          
     D  RRN                          10i 0                                                          
     D                                4                                                             
     D  BfrRecOfs                    10i 0                                                          
     D  BfrRecLen                    10i 0                                                          
     D  BfrNullOfs                   10i 0                                                          
     D  BfrNullLen                   10i 0                                                          
     D  AftRecOfs                    10i 0                                                          
     D  AftRecLen                    10i 0                                                          
     D  AftNullOfs                   10i 0                                                          
     D  AftNullLen                   10i 0                                                          
      *                                                                                             
     D  BfrRecPtr      S               *                                                            
     D  Original     E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(BfrRecPtr)                                         
     D                                     Qualified                                                
      *                                                                                             
     D  AftRecPtr      S               *                                                            
     D  New          E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(AftRecPtr)                                         
     D                                     Qualified                                                
      *                                                                                             
     D   Insert        C                   '1'                                                      
     D   Delete        C                   '2'                                                      
     D   Update        C                   '3'                                                      
     D   Read          C                   '4'                                                      
      *                                                                                             
     D   After         C                   '1'                                                      
     D   Before        C                   '2'                                                      
      *                                                                                             
     D   none          C                   '0'                                                      
     D   change        C                   '1'                                                      
     D   cs            C                   '2'                                                      
     D   all           C                   '3'                                                      
      *                                                                                             
      /Free                                                                                         
              BfrRecPtr = %addr(TrgBuffer) + TrgBuffer.BfrRecOfs;                                   
              AftRecPtr = %addr(TrgBuffer) + TrgBuffer.AftRecOfs;                                   
                                                                                                    
              dsply ('USRPRF ' +                                                                    
                      %trim(dsUser) + ' saw: ');                                                    
              dsply ('USRPRF ' +                                                                    
                      %trim(crUser) + ' saw: ');                                                    
              dsply ( %trim(TrgBuffer.Library) + '/' +                                              
                      %trim(TrgBuffer.File) + '(' +                                                 
                      %trim(TrgBuffer.Member) + '):' +                                              
                      'RRN=' +                                                                      
                      %trim(%char(TrgBuffer.RRN)) );                                                
              dsply (%trim(%char(Original.CUSNUM)) + ' ' +                                          
                     Original.LSTNAM +                                                              
                     ' ' + Original.STATE);                                                         
                                                                                                    
          //  *inLR = *on ;       //                                                                  
              return ;                                                                              
                                                                                                    
      /End-Free

確認

では、どういうふうに動くのか、確認してみましょう。

5250 画面から

DSPPFM コマンドで、トリガーが設定されているテーブルの内容を表示させてみます。

こんなかんじのメッセージが毎行をアクセスするために表示されていきます。
DSPPFM コマンドの処理結果が画面に表示されるより前に、各レコードは事前に一件一件アクセスされているわけですからね。

最後にまとめて表示結果が返ってきます。

実行中にジョブの呼び出しスタックを見てみると、ちゃんとトリガープログラムがスタックに乗っているのが確認できます。

クライアントから

「SQL スクリプトの実行」経由でも実行してみましょう。

DSPLY 命令の表示結果は QZDASOINIT ジョブから、デフォルトで QSYSOPR に送られます。
ちなみに、メッセージの表示順は下から上になっています。

最初に表示される dsUser が"QUSER"で、次の crUser が「SQL スクリプトの実行」を接続する時のユーザー、つまり実行ユーザーになっていることが確認できますね。

トリガー処理プログラムの中で System API を使用

さらに、System API を組み込んでみました。

最初の RtvJobInf プロシージャは、ジョブの実行情報を取得するものです。もともとユーザープロフィールを取ろうと思ったのですが、ジョブのユーザーで、実際の実行ユーザーではない場合があるので、プログラム状況データ構造に戻しています...... そういう意味では何にもこのプロシージャは使われていないわけなのですが、まぁ参考になるかなと思ってそのまま載せています。

取得した情報は DSPLY 命令ではなく、QMHSNDPM API を使って情報メッセージとして出力するように変更しました。

     H DFTACTGRP(*no)                                                                               
      *                                                                                             
     D                SDS                                                                           
     D  dsUser               254    263a                                                            
     D  crUser               358    367a                                                            
      *                                                                                             
     D Trg_SndMsg      PR                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          
      *                                                                                             
     D Trg_SndMsg      PI                                                                           
     D  TrgBuffer                          LIKEDS(TrgInfo)                                          
     D  TrgBufferLen                 10i 0                                                          
      *                                                                                             
     D TrgInfo         DS                  Qualified                                                
     D  File                         10a                                                            
     D  Library                      10a                                                            
     D  Member                       10a                                                            
     D  Event                         1a                                                            
     D  Time                          1a                                                            
     D  CmtLckLvl                     1a                                                            
     D                                3                                                             
     D  CCSID                        10i 0                                                          
     D  RRN                          10i 0                                                          
     D                                4                                                             
     D  BfrRecOfs                    10i 0                                                          
     D  BfrRecLen                    10i 0                                                          
     D  BfrNullOfs                   10i 0                                                          
     D  BfrNullLen                   10i 0                                                          
     D  AftRecOfs                    10i 0                                                          
     D  AftRecLen                    10i 0                                                          
     D  AftNullOfs                   10i 0                                                          
     D  AftNullLen                   10i 0                                                          
      *                                                                                             
     D  BfrRecPtr      S               *                                                            
     D  Original     E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(BfrRecPtr)                                         
     D                                     Qualified                                                
      *                                                                                             
     D  AftRecPtr      S               *                                                            
     D  New          E DS                  ExtName(QCUSTCDT)                                        
     D                                     Based(AftRecPtr)                                         
     D                                     Qualified                                                
      *                                                                                             
     D   Insert        C                   '1'                                                      
     D   Delete        C                   '2'                                                      
     D   Update        C                   '3'                                                      
     D   Read          C                   '4'                                                      
      *                                                                                             
     D   After         C                   '1'                                                      
     D   Before        C                   '2'                                                      
      *                                                                                             
     D   none          C                   '0'                                                      
     D   change        C                   '1'                                                      
     D   cs            C                   '2'                                                      
     D   all           C                   '3'                                                      
      *                                                                                             
     D RtvJobInf       PR                  EXTPGM('QUSRJOBI')                                       
     D  RcvVar                    65535a   Options( *VarSize)                                       
     D  RcvVarLen                    10i 0 Const                                                    
     D  FmtName                       8a   Const                                                    
     D  QualJobName                  26a   Const                                                    
     D  InternalJobID                16a   Const                                                    
     D  ErrorCode                          like(APIErr)                                             
      *                                                                                             
     D QualJobName     DS            26    Qualified                                                
     D  JobName                      10a   Inz('*')                                                 
     D  UserName                     10a   Inz(*blanks)                                             
     D  JobNumber                     6a   Inz(*blanks)                                             
      *                                                                                             
     D JOBI0400        DS                  Qualified                                                
     D  NbrBytesRtn                  10i 0                                                          
     D  NbrBytesAvl                  10i 0                                                          
     D  JobName                      10a                                                            
     D  UsrName                      10a                                                            
     D  JobNbr                        6a                                                            
     D  InternalJobID                16a                                                            
     D  JobSts                       10a                                                            
     D  JobType                       1a                                                            
     D  JobSubType                    1a                                                            
     D  InTimestamp                  13a                                                            
     D  ActTimestamp                 13a                                                            
     D  JobAcctCode                  15a                                                            
     D  JobDName                     10a                                                            
     D  JobDLName                    10a                                                            
     D  UOWID                        24a                                                            
     D  ModeName                      8a                                                            
     D  InqMsgRpy                    10a                                                            
     D  LogCLPgm                     10a                                                            
     D  BrkMsg                       10a                                                            
     D  StsMsg                       10a                                                            
     D  DevRcyAcn                    13a                                                            
     D  DDMCnv                       10a                                                            
     D  DatSep                        1a                                                            
     D  DatFmt                        4a                                                            
     D  PrtTxt                       30a                                                            
     D  SbmJobName                   10a                                                            
     D  SbmUsrName                   10a                                                            
     D  SbmJobNbr                     6a                                                            
     D  SbmMsgqName                  10a                                                            
     D  SbmMsgqLName                 10a                                                            
     D  TimSep                        1a                                                            
     D  CCSID                        10i 0                                                          
     D  DatTimScdJob                  8a                                                            
     D  PrtKeyFmt                    10a                                                            
     D  SrtSeqTbl                    10a                                                            
     D  SrtSeqLib                    10a                                                            
     D  LangID                        3a                                                            
     D  CntryID                       2a                                                            
     D  CompSts                       1a                                                            
     D  SignonJob                     1a                                                            
     D  JobSwt                        8a                                                            
     D  JobMsgqFl                    10a                                                            
     D                                1a                                                            
     D  JobMsgqMx                    10i 0                                                          
     D  DftCCSID                     10i 0                                                          
     D  RtgDta                       80a                                                            
     D  DecFmt                        1a                                                            
     D  ChrIdCtl                     10a                                                            
     D  SvrType                      30a                                                            
     D  AlwMltThd                     1a                                                            
     D  JobLodPnd                     1a                                                            
     D                                1a                                                            
     D  JobEndReason                 10i 0                                                          
     D  EJobType                     10i 0                                                          
     D  DatTimeJobEnd                13a                                                            
     D                                1a                                                            
     D  SplFAcn                      10a                                                            
     D  Ofs2ASPinf                   10i 0                                                          
     D  NbrEntASPinf                 10i 0                                                          
     D  EntryLen                     10i 0                                                          
     D  TimeZoneD                    10a                                                            
     D  JobLog                       10a                                                            
      *                                                                                             
     D APIErr          DS                  Qualified                                                
     D  ErrSize                      10i 0 inz(%size(APIErr))                                       
     D  ErrLen                       10i 0 inz(0)                                                   
     D  ErrID                         7a                                                            
     D  rsvd                          1a                                                            
     D  ErrData                   32767a                                                            
      *                                                                                             
     D RcvSize         S             10  0 INZ(16776704)                                            
      *                                                                                             
     D HandleErr       PR                                                                           
      *                                                                                             
     D SndPgmMsg       PR                  EXTPGM('QMHSNDPM')                                       
     D  MsgID                         7a   Const                                                    
     D  QualMsgF                     20a   Const                                                    
     D  MsgDta                      256a   Const                                                    
     D  MsgDtaLen                    10i 0 Const                                                    
     D  MsgType                      10a   Const                                                    
     D  CallStkEntry                 10a   Const                                                    
     D  CallStkCount                 10i 0 Const                                                    
     D  MsgKey                        4a   Const                                                    
     D  ErrorCode                          like(APIErr)                                             
      *                                                                                             
     D MSG             S           3000a   inz(*blanks)                                             
     D MsgKey          S              4a                                                            
      *                                                                                             
      /Free                                                                                         
              BfrRecPtr = %addr(TrgBuffer) + TrgBuffer.BfrRecOfs;                                   
              AftRecPtr = %addr(TrgBuffer) + TrgBuffer.AftRecOfs;                                   
                                                                                                    
              RtvJobInf( JOBI0400 :                                                                 
                    RcvSize :                                                                       
                    'JOBI0400' :                                                                    
                    QualJobName :                                                                   
                    *blanks :                                                                       
                    APIErr );                                                                       
                                                                                                    
              If APIErr.ErrLEN <> 0 ;                                                               
                HandleErr() ;                                                                       
                return ;                                                                            
              endif ;                                                                               
                                                                                                    
         //   MSG = ('USRPRF ' +                            //                                   
         //           %trim(JOBI0400.UsrName) + ' saw: ' +  //                                   
              MSG = ('USRPRF ' +                                                                    
                      %trim(crUser) + ' saw: ' +                                                    
                      %trim(TrgBuffer.Library) + '/' +                                              
                      %trim(TrgBuffer.File) + '(' +                                                 
                      %trim(TrgBuffer.Member) + '):' +                                              
                      'RRN=' +                                                                      
                      %trim(%char(TrgBuffer.RRN))) +  ' ' +                                         
                     (%trim(%char(Original.CUSNUM)) + ' ' +                                         
                     Original.LSTNAM +                                                              
                     ' ' + Original.STATE);                                                         
                                                                                                    
              SndPgmMsg( 'CPF9897' :                                                                
                    'QCPFMSG   *LIBL' :                                                             
                    %trim(MSG) :                                                                    
                    %len(%trim(MSG)) :                                                              
                    '*INFO' :                                                                       
                    '*PGMBDY' :                                                                     
                    1 :                                                                             
                    MsgKey :                                                                        
                    APIErr );                                                                       
                                                                                                    
         //   *inLR = *on ;                                     //                                  
              return ;                                                                              
                                                                                                    
      /End-Free                                                                                     
      *                                                                                             
      *                                                                                             
     P HandleErr       B                                                                            
     D HandleErr       PI                                                                           
      *                                                                                             
      /Free                                                                                         
                                                                                                    
      /End-Free                                                                                     
      *                                                                                             
     P HandleErr       E

確認

RUNQRY コマンドでもちゃんとトリガーは起動されます。

こちらは RUNQRY の実行結果です。

トリガーからの出力はジョブログに載っています。

[Top Pageに戻る]

Ads by TOK2