ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

How to compare 2 data structures field by field?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    For some reasons, we tend to avoid SQL programs. (Been the practice). So, i initially went ahead with QUSLFLD API. But, again I needed to capture the length offset/field names into a new array/DS and then proceed from there. I was also contemplating invoking Java procedures and let Java handle the DS comparisons. (We do use quite a lot of Java calls for our programs). However, the method you gave me is really the simplest solution of all, and works like a charm. Thanks again.

    Comment


    • #17
      Adding Java just adds complexity with no benefits that I can think of. You could do stuff with PHP or Python (for example) since they can use dynamic field names to access data - but you'd have to use (say) DATA-GEN with the DS to generate json or XML so that the PHP/Python script would have something to work with. Be way more work than needed - not to mention that none of those languages really understand packed and zoned numbers in the way that RPG does.

      And there more I think about it the more reasons I find not to bother ...

      Anyway - thanks for providing article fodder. I'll let you know when it is published.

      Comment


      • #18
        Originally posted by JonBoy View Post
        Adding Java just adds complexity with no benefits that I can think of. You could do stuff with PHP or Python (for example) since they can use dynamic field names to access data - but you'd have to use (say) DATA-GEN with the DS to generate json or XML so that the PHP/Python script would have something to work with. Be way more work than needed - not to mention that none of those languages really understand packed and zoned numbers in the way that RPG does.

        And there more I think about it the more reasons I find not to bother ...

        Anyway - thanks for providing article fodder. I'll let you know when it is published.
        I could have tried DATA-GEN but then, its not supported in V7R2 and below. Please keep me updated once you publish the article. Eager to learn

        Comment


        • #19
          Hi JonBoy, Can you provide me your expertise on one more query that I have?

          As you know from the code, Data structure BEFORE is based on an external file - FILE1. One of the field in this file is defined as GRAPHIC and of length 10.

          When the below condition is satisfied, I have to write this particular field into my LOG file which has all fields as CHARACTERS. How can I convert the graphics to character type? I tried %Graph %UCS2 and %Char functions but no luck.

          if %subst(after:1:10) <> %subst(before: 1:10); Write this particular field to log file EndIf;

          Comment


          • #20
            The only thing I can think of off the top of my head is to do something like this:

            Code:
                   dcl-ds remap;
                     inpChar     char(10);
                     outGraph    graph(5)  Overlay(inpChar);
                   end-ds;
            Basically you take the character substring and place it in inpChar - you can then use the outGraph string.

            Considering that a graphic field has to include a shift-in/shift-out pair 5 characters seems awfully small. Whatever useful double-byte data is stored there?

            P.S. Look at the values in the de=bugger in hex form and see what the actual content is.


            Comment


            • #21
              Not 100% completed - but here's the base code that I'll be referencing in the upcoming article. Thanks for the inspiration!

              Note that it handles and reports on both packed and zoned numerics. There are a lot of comments so hopefully it will be understandable even without the accompanying article.

              Code:
              **Free
              
              // This code is provided only as a teaching example of a concept
              // It is not complete and should not be used in production code
              
              ctl-opt  Option(*NoDebugIO : *SrcStmt )  DftActgrp(*No);
              ctl-opt CCSID(*GRAPH:*JOBRUN);
              
              // FieldData is the oufile from:
              //   DSPFFD FILE(PRODUCT) OUTPUT(*OUTFILE) OUTFILE(FIELDDATA)
              // Normally that command would be built in to this program via QCMDEXC
              //   or system() but we're "cheating" here to keep the example short.
              
              dcl-f  FieldData;
              
              // These are the DS images of the before and after state of the records
              // They could be pointer mapped to trigger buffer images, or mapped from
              //    journal records, or simply be before and after images from an
              //    operator's screen. Basically anything where you need to know what changed
              // For this proof of concept example we will simply hard-code some values
              //    in the "after" DS to ensure a difference between "before" and "after"
              
              dcl-ds  before  ExtName('PRODUCTEXT')  Qualified  Inz  end-ds;
              
              dcl-ds  after   ExtName('PRODUCTEXT')  Qualified  Inz  end-ds;
              
              // The following DS array contains the details of the fields in
              //   the DS. Their name, data type, position, etc.
              // It provides the data needed for comparing before and after images
              // In this case it was built from DSPFFD data but it could have
              //   been from (say) SYSCOLUMNS2 or the QUSLFLD API.
              
              dcl-ds  fieldDetail  Dim(99)  Qualified;
                 startPosn  Like(WHIBO);   // Offset within the record
                 length     like(WHFLDB);  // Length in bytes
                 name       like(WHFLDI);
                 dataType   like(WHFLDT);
                 decimals   like(WHFLDP);  // Number of decimal places
              End-Ds;
              
              dcl-s  fieldBefore  varchar(256);  // This length assumes that no fields
              dcl-s  fieldAfter   varchar(256);  //   will exceed 256 bytes in length
                                                 // Adjust size as needed
              
              // This structure is used to enable the bytes that represent a
              //   numeric field's value to be processed as numeric in
              //   this program. It basically re-maps bytes as packed or zoned numeric
              
              dcl-ds  mapNumeric;
                 zonedValue   zoned(15);
                 packedValue  packed(29) Overlay(zonedValue);
              end-Ds;
              
              dcl-s decimalValue  packed(29:5);
              
              dcl-s  i      int(5);
              dcl-s  count  int(5);
              dcl-s  start  int(5);
              
              // Data types for numeric fields - this code assumes that only
              //   character/packed/zoned fields are present in the DS
              dcl-c PACKED  'P';
              dcl-c ZONED   'S';
              
              // This loop simply reads the file produced by DSPFFD and stores
              //   the required data in the fieldData array for later reference
              // Normally this code would be in a Service Program and might use
              //   SQL or API calls as noted earlier.
              
              read FieldData;
              
              DoU  %Eof(FieldData);
                 count += 1;
                 fieldDetail(count).name      = WHFLDI;
                 fieldDetail(count).dataType  = WHFLDT;
                 fieldDetail(count).startPosn = WHIBO;
                 fieldDetail(count).length    = WHFLDB;
                 fieldDetail(count).dataType  = WHFLDT;
                 fieldDetail(count).decimals  = WHFLDP;
              
                 read FieldData;
              
              EndDo;
              
              // To show the logic works we'll change some values in DS after.
              //   The rest of the after DS & all of before DS has blanks/zeros
              
              after.STOH    = 99;
              after.SELLPR  = 123.45;
              after.MAXDISC = 9.999;
              
              // This loop will process each field in turn reporting on
              //   any that have changed.
              
              For i = 1 to count;
                 // Copy relevant portions of before/after images to a
                 //   temporary field to simplify subsequent logic
                 fieldAfter  = %subst(after:fieldDetail(i).startPosn:fieldDetail(i).length);
                 fieldBefore = %subst(before:fieldDetail(i).startPosn:fieldDetail(i).length);
              
                 if fieldAfter <> fieldBefore;
                    // Field has changed so report it
                    // Replace with any logic needed to report before & after values
                    Dsply ('Field ' + fieldDetail(i).name + ' has changed');
              
                    // Determine where numeric field data starts in the
                    //   mapNumeric DS.
                    start = %Len(mapNumeric) - fieldDetail(i).length + 1;
              
                    // To handle packed and numeric values we convert them
                    //   and scale them so that we can display them. The logic
                    //   here is intended to give you ideas - it is not a definitive
                    //   solution for all data types and sizes.
              
                    If fieldDetail(i).dataType = PACKED;
                       packedValue = 0; // Initialize packed work field
                       %subst(mapNumeric: start ) = fieldAfter;
                       // Now scale the numeric value to account for decimals
                       If fieldDetail(i).decimals > 0;
                          decimalValue = packedValue / (10 ** fieldDetail(i).decimals);
                       Else;
                          decimalValue = packedValue;
                       EndIf;
                       Dsply ('New value ' + %Char(decimalValue) ) ;
              
                    ElseIf fieldDetail(i).dataType = ZONED;
                       zonedValue = 0; // Initialize all positions in zoned work field
                       %subst(mapNumeric: start ) = fieldAfter;
                       If fieldDetail(i).decimals > 0;
                         decimalValue = zonedValue / (10 ** fieldDetail(i).decimals);
                       Else;
                          decimalValue =zonedValue;
                       EndIf;
                       Dsply ('New value ' + %Char(decimalValue) ) ;
              
                    EndIf;
              
                 EndIf;
              
              EndFor;
              
              *InLr = *On;
              Last edited by JonBoy; November 25, 2019, 12:10 PM.

              Comment

              Working...
              X