midrange.com code scratchpad
Name:
Trigger prohibits update - example
Scriptlanguage:
Plain Text
Tabwidth:
4
Date:
10/05/2015 03:20:00 pm
IP:
Logged
Description:
Example code of a Before Trigger which prohibits the update of a database row.
Code:
  1. PF DATESAMPLE:
  2.  
  3.      A          R DATER                     TEXT('Sample date field comparison')
  4.      A*
  5.      A            CHAR          10A         COLHDG('Char' 'data type')
  6.      A                                      ALWNULL
  7.      A            DATE1           L         COLHDG('Date' 'data type')
  8.      A                                      ALWNULL
  9.      A            DATE2          8S 0       COLHDG('Signed' 'data type')
  10.      A                                      EDTWRD('0    /  /  ')
  11.      A                                      ALWNULL
  12.      A            DATE4          8A         COLHDG('CHAR' 'data type'  )
  13.      A            TIME1           T         COLHDG('Time' 'data type')
  14.      A                                      ALWNULL
  15.      A            TIME2          6S 0       COLHDG('Signed' 'data type')
  16.      A                                      EDTWRD('0  :  :  ')
  17.      A                                      ALWNULL
  18.      A            TIMEST          Z         COLHDG('Timestamp' 'data type')
  19.      A                                      ALWNULL
  20.      A            PACKED         5P 0       COLHDG('Pack' 'data type')
  21.      A                                      EDTCDE(M)
  22.      A                                      ALWNULL
  23.      A            BINARY         9B 0       COLHDG('Binary' 'data type')
  24.      A                                      EDTCDE(M)
  25.      A                                      ALWNULL
  26.      A            DATE3           L         COLHDG('Date' 'data type')
  27.      A                                      DATFMT(*YMD)
  28.      A                                      DFT('99/01/01')
  29.      A                                      ALWNULL
  30.      A            CHARUTF8      10A         COLHDG('UTF8')
  31.      A                                      CCSID(1208)
  32.      A                                      ALWNULL
  33.      A            CHARUTF16     10G         COLHDG('UTF16')
  34.      A                                      CCSID(1200)
  35.      A                                      ALWNULL
  36.  
  37. ADDPFTRG FILE(BUCK/datesample) TRGTIME(*BEFORE) 
  38.          TRGEVENT(*UPDATE) ALWREPCHG(*YES)      
  39.          PGM(BUCK/TRIGGER) RPLTRG(*YES)         
  40.  
  41. This first RPG program is part of a trigger mediator; it allows me to turn the trigger on and off via a data area instead of needing a *EXCL lock on the table to RMVPFTRG.
  42.  
  43. RPGLE program TRIGGER:
  44.  
  45.      h dftactgrp(*no) actgrp(*CALLER) option(*srcstmt: *nodebugio)
  46.      h bnddir('BUCK') alwnull(*usrctl)
  47.      h debug
  48.  
  49.       * DBGVIEW(*list)
  50.  
  51.       * Sample "self-updating" trigger program.  'Self-updating' means this trigger
  52.       * updates the file it's attached to; modifying the record being updated.
  53.       *   runs as a two part process.  When the file is updated, the trigger fires and
  54.       *   executes the first program.  This is an "on/off" switch, and only passes
  55.       *   parms back and forth to the second program, which actually does the trigger
  56.       *   processing.
  57.  
  58.       * DATESAMPLE trigger: Fires before update
  59.       *
  60.       *   ADDPFTRG FILE(BUCK/datesample) TRGTIME(*BEFORE)
  61.       *            TRGEVENT(*UPDATE) ALWREPCHG(*YES)
  62.       *            PGM(BUCK/TRIGGER) RPLTRG(*YES)
  63.  
  64.       /copy qprotosrc,buck
  65.  
  66.      d trgBufTemplatee ds                  EXTNAME(Trigger)
  67.      d trgStatus                      1a   dtaara('TRIGGER')
  68.  
  69.      d trgBuf          s                   like(trgBufTemplate)
  70.      d trgBufLen       s             10u 0
  71.      d trgRtnCode      s             10i 0
  72.      d callLib         s             10a
  73.      d callPgm         s             10a
  74.      d callMod         s             10a
  75.      d callPrc         s            256a   varying
  76.  
  77.      dTRIGGERRUN       pr                  extpgm('TRIGGERRUN')
  78.      d trgBuf                              like(trgBufTemplate)
  79.      d trgBufLen                     10u 0
  80.      d trgRtnCode                    10i 0
  81.  
  82.      dsndMsg           pr
  83.      d errMsgId                                  like(trgRtnCode)
  84.  
  85.       * Input/output parameters
  86.      c     *entry        plist
  87.      c                   parm                    trgBuf
  88.      c                   parm                    trgBufLen
  89.  
  90.       /free
  91.  
  92.        monitor;
  93.  
  94.        // fail silently if we can't find the data area.  If we can't figure out
  95.        // whether to fire the trigger or not, default to NOT.
  96.        in(e) trgStatus;
  97.        if %error();
  98.          return;
  99.        endif;
  100.  
  101.        // see if we should fire the trigger
  102.        if trgStatus = 'Y';
  103.          callp(e) getProcName(callLib: callPgm: callMod: callPrc);
  104.          //dump(a);
  105.          callp(e) TRIGGERRUN (trgBuf: trgBufLen: trgRtnCode);
  106.  
  107.          // fail silently if we can't run the actual trigger processing pgm
  108.          if %error();
  109.            return;
  110.          endif;
  111.  
  112.          // trigger fell over - tell DB2 the action failed
  113.          if trgRtnCode <> 0;
  114.            sndMsg(trgRtnCode);
  115.          endif;
  116.  
  117.        endif;
  118.  
  119.        // not LR, but RETURN.  Saves re-initialising every time I'm called.
  120.        return;
  121.  
  122.        on-error *all;
  123.          // violates the principle of actually handling every error
  124.          // on purpose - we want to silently fail and not break the DB
  125.          return;
  126.  
  127.        endmon;
  128.  
  129.       /end-free
  130.  
  131.      psndMsg           b
  132.      dsndMsg           pi
  133.      d errMsgId                                  like(trgRtnCode)
  134.  
  135.       * Send message API parameters
  136.       *   stack count reflects the fact that we need to send the message
  137.       *   up the stack; i.e. not this pgm, but its caller.  Remember that we're
  138.       *   down another level because of the subprocedure...
  139.      d msgId           s              7    inz('CPF9898')
  140.      d msgFil          s             20    inz('QCPFMSG   *LIBL     ')
  141.      d msgData         s             80
  142.      d msgDataLen      s             10i 0 inz(%len(msgData))
  143.      d msgType         s             10    inz('*ESCAPE')
  144.      d msgStackEnt     s             10    inz('*')
  145.      d msgStackCnt     s             10i 0 inz(3)
  146.      d msgKey          s              4
  147.      d msgErrStruc     s                   like(errStruc)
  148.  
  149.       * API error structure
  150.      d errStruc        ds                  inz
  151.      d  errSSize                     10i 0 inz(%len(errStruc))
  152.      d  errSUse                      10i 0
  153.      d  errSMsgID                     7
  154.      d  errSResrv                     1
  155.      d  errSData                     80
  156.  
  157.      dQMHSNDPM         pr                  extpgm('QMHSNDPM')
  158.      d msgId                          7
  159.      d msgFil                        20
  160.      d msgData                       80
  161.      d msgDataLen                    10i 0
  162.      d msgType                       10
  163.      d msgStackEnt                   10
  164.      d msgStackCnt                   10i 0
  165.      d msgKey                         4
  166.      d msgErrStruc                         like(errStruc)
  167.      d
  168.      d
  169.  
  170.       /free
  171.  
  172.        monitor;
  173.  
  174.        // sending an escape message tells DB2 that the update did not occur
  175.        msgData  = 'Trigger failed: ' + %trim(%char(errMsgId));
  176.        msgErrStruc = errStruc;
  177.  
  178.        callp(e) QMHSNDPM (msgId:
  179.                           msgFil:
  180.                           msgData:
  181.                           msgDataLen:
  182.                           msgType:
  183.                           msgStackEnt:
  184.                           msgStackCnt:
  185.                           msgKey:
  186.                           msgErrStruc);
  187.  
  188.        errStruc = msgErrStruc;
  189.  
  190.        on-error *all;
  191.          // violates the principle of actually handling every error
  192.          // on purpose - we want to silently fail and not break the DB
  193.          return;
  194.  
  195.        endmon;
  196.  
  197.       /end-free
  198.      p                 e
  199.  
  200. This next RPG program is the actual trigger logic.  When this code issues an ESCAPE message, it percolates back to the top level program that issued the I/O which fired the trigger.
  201.  
  202. RPGLE program TRIGGERRUN:
  203.  
  204.      h dftactgrp(*no) actgrp(*CALLER) option(*srcstmt: *nodebugio)
  205.      h copyright('Copyright 2000, 2009 Buck Calabro')
  206.      h debug
  207.  
  208.       * DBGVIEW(*list)
  209.  
  210.       * Sample "self-updating" trigger program
  211.       *   runs as a two part process.  When the file is updated, the trigger fires and
  212.       *   executes the first program.  This is an "on/off" switch, and only passes
  213.       *   parms back and forth to the second program, which actually does the trigger
  214.       *   processing.
  215.  
  216.       * ===========================================================
  217.       * Specific to this DBF
  218.       * ===========================================================
  219.  
  220.       * Map the file I/O buffers from the external definitions
  221.      d OldRcdImg     E DS                  EXTNAME(DATESAMPLE)
  222.      d                                     based(pOldRcd)
  223.      d                                     prefix(O_)
  224.  
  225.      d NewRcdImg     E DS                  EXTNAME(DATESAMPLE)
  226.      d                                     based(pNewRcd)
  227.      d                                     prefix(N_)
  228.  
  229.       * ===========================================================
  230.       * Generic to all triggers:
  231.       * ===========================================================
  232.  
  233.       * The DS definition has a field that pushes the
  234.       * DS length to 32766.  This field is only there for
  235.       * debugging purposes, and can be ommitted if desired.
  236.       * Never use that field to access the buffers; always
  237.       * use the data structures defined above with the EXTNAME
  238.       * of the physical file being triggered.
  239.      d TrgBuf        E DS                  EXTNAME(Trigger)
  240.  
  241.       * We can't tell how many fields are in the file;
  242.       * to avoid hard-coding the number, set up an arbitrarily large
  243.       * array to hold the null map.  Remember that you can only
  244.       * trust the array up to the null map length!!!
  245.      d OldNulImg       ds         32766    Based(POldNul)
  246.      d  OldNulMap                     1a   dim(%size(OldNulImg))
  247.      d NewNulImg       ds         32766    Based(PNewNul)
  248.      d  NewNulMap                     1a   dim(%size(NewNulImg))
  249.  
  250.       * Local work variables
  251.      d TrgBufLen       S             10u 0
  252.      d TrgRtnCde       S             10i 0
  253.  
  254.       * Named constants
  255.       *   Commit lock level
  256.       *     *NONE, *CHG, *CS, *ALL
  257.      d CLNONE          S              1A   INZ('0')
  258.      d CLCHG           S              1A   INZ('1')
  259.      d CLCS            S              1A   INZ('2')
  260.      d CLALL           S              1A   INZ('3')
  261.  
  262.       *   Trigger event = Insert
  263.       *     Insert, Delete, Update
  264.      d TRGINS          S              1A   INZ('1')
  265.      d TRGDEL          S              1A   INZ('2')
  266.      d TRGUPD          S              1A   INZ('3')
  267.  
  268.       *   Trigger time
  269.       *     Before, After
  270.      d TRGBEF          S              1A   INZ('1')
  271.      d TRGAFT          S              1A   INZ('2')
  272.  
  273.       *   Null
  274.      d NULLNO          S              1A   INZ('0')
  275.      d NULLYES         S              1A   INZ('1')
  276.  
  277.       * Input/output parameters
  278.      c     *entry        plist
  279.      c                   parm                    TrgBuf
  280.      c                   parm                    TrgBufLen
  281.      c                   parm                    TrgRtnCde
  282.  
  283.  
  284.       /FREE
  285.        monitor;
  286.        // Load the working record buffers and null maps
  287.        // Reiterate the warning about obeying the limits of the buffer
  288.        // as set by the "length" variables.  Because we're using pointers,
  289.        // we can wander off into "memory unknown" if we fail to observe
  290.        // these limits!!!
  291.        pOldRcd = %addr(TrgBuf)+ OldRecOff;
  292.        pNewRcd = %addr(TrgBuf)+ NewRecOff;
  293.  
  294.        // To manipulate the null indicator for a field,
  295.        // set the null map value.  Don't try to fiddle with %nullind
  296.        // because you can't have a null capable data structure subfield.
  297.        pOldNul = %addr(TrgBuf)+ OldNulOff;
  298.        pNewNul = %addr(TrgBuf)+ NewNulOff;
  299.  
  300.        // ===========================================================
  301.        // Specific to this DBF
  302.        // ===========================================================
  303.  
  304.        // Test conditions
  305.        // NOTE NOTE NOTE - ALWAYS check the null indicator!
  306.        if event = TRGINS or
  307.           event = TRGUPD;
  308.          select;
  309.            // Here, if the name is left blank, we'll populate it
  310.            // with an eye-catcher so we know the trigger works.
  311.            when NewNulMap(1)=NULLNO and N_Char=*Blanks;
  312.              N_Char='Trigger x';
  313.            when NewNulMap(1)=NULLNO and N_Char='*blanks';
  314.              N_Char='Trigger y';
  315.            when NewNulMap(1)=NULLNO and N_Char='NULL';
  316.              NewNulMap(1) = NULLYES;
  317.              N_Char = 'Now null';
  318.            // Here, if the name is '01', we'll fail the update
  319.            // This is to test trigger failure
  320.            when NewNulMap(1)=NULLNO and N_Char='01';
  321.              trgRtnCde = 1;
  322.            when NewNulMap(1)=NULLYES;
  323.              NewNulMap(7) = NULLNO;
  324.              N_Packed = 12345;
  325.          endsl;
  326.  
  327.        endif;
  328.  
  329.        // not LR, but RETURN.
  330.        return;
  331.  
  332.        on-error *all;
  333.          // violates the principle of actually handling every error
  334.          // on purpose - we want to silently fail and not break the DB
  335.          trgRtnCde = -1;
  336.          return;
  337.  
  338.        endmon;
  339.  
  340.       /END-FREE 
  341.  
  342. Finally, the program that tests the trigger environment.  This tries to update a row in DATESAMPLE and the trigger logic is coded to check for this specific updtae, and prevent it from happening.  This program receives the various messages which occur during this situation.
  343.  
  344. RPGLE program TRIGTEST
  345.  
  346.      h/copy qrpglesrc,stdhspec
  347.      fdatesampleuf   e             disk
  348.  
  349.         dcl-ds QUSEC qualified;
  350.            errBytesProv int(10) inz(%size(qusec));
  351.            errBytesAvail int(10) inz;
  352.            errMsgID char(7);
  353.            reserved char(1);
  354.            errMsgDta char(512);
  355.         end-ds;
  356.  
  357.         dcl-ds rcvm0200 inz qualified;
  358.           returned int(10);
  359.           available int(10);
  360.           msgSev int(10);
  361.           msgId char(7);
  362.           msgType char(2);
  363.           msgKey char(4);
  364.           msgf char(10);
  365.           msgfLib char(10);
  366.           msgfLibUsed char(10);
  367.           sendJob char(10);
  368.           sendUser char(10);
  369.           sendNbr char(6);
  370.           sendPgm char(12);
  371.           sendInstr char(4);
  372.           sendDate char(7);
  373.           sendTime char(6);
  374.           rcvPgm char(10);
  375.           rcvInstr char(4);
  376.           sendType char(1);
  377.           rcvType char(1);
  378.           reserved char(1);
  379.           textConvCCSID int(10);
  380.           dataConvCCSID int(10);
  381.           alertOpt char(9);
  382.           msgCCSID int(10);
  383.           dataCCSID int(10);
  384.           dataLenReturn int(10);
  385.           dataLenAvail int(10);
  386.           msgLenReturn int(10);
  387.           msgLenAvail int(10);
  388.           helpLenReturn int(10);
  389.           helpLenAvail int(10);
  390.           msgData char(0512);
  391.           message char(0512);
  392.           msgHelp char(0512);
  393.         end-ds;
  394.  
  395.         dcl-pr rcvPgmMsg extpgm('QMHRCVPM');
  396.            messageInfo char(1) options(*varsize);
  397.            messageInfoLen int(10) const;
  398.            fmtName char(8) const;
  399.            callStackEntry char(65535) const options(*varsize);
  400.            callStackCounter int(10) const;
  401.            msgType char(10) const;
  402.            msgKey char(4);
  403.            waitTime int(10) const;
  404.            msgAction char(10) const;
  405.            errorStruc likeds(qusec);
  406.         end-pr;
  407.  
  408.         dcl-s messageInfoLen int(10) inz(%size(rcvm0200));
  409.         dcl-s fmtName char(8) inz('RCVM0200');
  410.         dcl-s callStackEntry char(10) inz('*');
  411.         dcl-s callStackCounter int(10) inz(0);
  412.         dcl-s msgType char(10) inz('*LAST');
  413.         dcl-s msgKey char(4) inz(*blanks);
  414.         dcl-s waitTime int(10) inz(0);
  415.         dcl-s msgAction char(10) inz('*OLD');
  416.  
  417.        // this is a simple update which will fire my trigger.
  418.        // the trigger will deliberately fall over
  419.        chain 2 dater;
  420.        if %found();
  421.          char = '01';
  422.          update(e) dater;
  423.  
  424.          // get the error message details
  425.          if %error();
  426.            // the 'C'
  427.            rcvPgmMsg(rcvm0200: messageInfoLen: fmtName:
  428.                      callStackEntry: callStackCounter:
  429.                      msgType: msgKey: waitTime: msgAction: qusec);
  430.            // CPF502B- Error in trigger program
  431.            msgType = '*PRV';
  432.            msgKey = rcvm0200.msgKey;
  433.            rcvPgmMsg(rcvm0200: messageInfoLen: fmtName:
  434.                      callStackEntry: callStackCounter:
  435.                      msgType: msgKey: waitTime: msgAction: qusec);
  436.            // my CPF9898 sent by the trigger - 02, diagnostic
  437.            msgType = '*PRV';
  438.            msgKey = rcvm0200.msgKey;
  439.            rcvPgmMsg(rcvm0200: messageInfoLen: fmtName:
  440.                      callStackEntry: callStackCounter:
  441.                      msgType: msgKey: waitTime: msgAction: qusec);
  442.            dump(a);
  443.          endif;
  444.  
  445.        endif;
  446.  
  447.        *inlr = *on; 
© 2004-2019 by midrange.com generated in 0.008s valid xhtml & css