ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Length of varying length variable is out of range.

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

  • Length of varying length variable is out of range.

    Hello all!

    I'm creating a program that allows a user to enter an account number (the account number length varies) and then have the program return a concatenated string of email addresses that are associated with the aforementioned account number. Whenever I CALL FOECADDRPG PARM('38539') and step through my program I run into this error, "Length of varying length variable is out of range", on the first line after /Free (Thanks to JonBoy I know that /Free is no longer required however my higher-ups insist I do it anyway).

    I am still playing around with the tools commonly used in RPG Free and, since this is my first time learning a programming language, I probably am not using them correctly (this program is my first time playing around with a "FOR" loop as well as an "ARRAY") so feel free to critique away!


    PHP Code:
    *--------------------------------------------------------------------------------------------*
          *--------------------------------------------------------------------------------------------*
          *
    Files
           Dcl
    -F FOECADD DISK(*EXTkeyed Usage(*INPUT);
          *--------------------------------------------------------------------------------------------*
          *--------------------------------------------------------------------------------------------*
          *
    Standalones
           Dcl
    -S ACCT                     VARCHAR(20);
           
    Dcl-S X                             Packed(2);
           
    Dcl-S DSPVAR               VARCHAR(50);
           
    Dcl-S KADBKNM          LIKE(ADBKNM);
           
    Dcl-s KADKEY1             LIKE(ADKEY1);
          *--------------------------------------------------------------------------------------------*
          *--------------------------------------------------------------------------------------------*
          *
    Plist
           Dcl
    -Pi FOECADDRPG ExtPgm;
             
    ACCTP VARCHAR(12);
           
    End-Pi;
          *--------------------------------------------------------------------------------------------*
          *--------------------------------------------------------------------------------------------*
          *
    Data Structures
           Dcl
    -Ds EmailArray;
            Array           
    VARCHAR(50Dim(15);
           
    END-DS;
          *--------------------------------------------------------------------------------------------*
          *--------------------------------------------------------------------------------------------*

           
    //MAINLINE PROCESSING
           
    /Free
            ACCT 
    '%' + %TRIM(ACCTP) + '%';    <--Length of varying length variable is out o
            KADBKNM 
    = *BLANKS;
            
    KADKEY1 = *BLANKS;
            
    EmailArray = *BLANKS;
            Array = *
    Blanks;

            
    EXEC SQL
              SELECT ADBKNM
    ,ADKEY1 into :KADBKNM,:KADKEY1
                FROM FOECADD
                WHERE ADKEY1 like 
    :ACCT;

            
    Dou %Eof(FOECADD);
            
    Chain (KADBKNM:KADKEY1FOECADD;
             IF %
    found(FOECADD);
               For 
    1 to %Elem(Array);
                 Array(
    X) = ADEMAD ' ; ';
               ENDFOR;

             ENDIF;

            
    ENDDO;

            
    DSPVAR EmailArray;
            
    Dsply DSPVAR;

           *
    InLR = *On;

           /
    End-Free 
    As always, thanks for the help and words of encouragement!

    -TJ

  • #2
    I varying field has the length at the beginning (2 or 4 byte interger IIRC). If you pass in '38539', it's likely interpreting the first two characters as a binary integer.

    Comment


    • jtaylor___
      jtaylor___ commented
      Editing a comment
      s/s "A varying field..."

  • #3
    Basically you would not normally use a VarChar as a parm for any parameter that is expected to come from the command line because as jtaylor has pointed out there has to be a count in the first two (or four) bytes. This is tough to do from the command line. If the program is called from another program no problem, if you must do it from the command line then create a command option to accept the parm and call the program.

    For this kind of task it is usually better to accept as a parm a fixed length filed of the maximum length and then %Trim it into a varchar if needed. In fact your logic is already doing a trim on the parm so just change the parm definition.

    Comment


    • #4
      Hey Tho_Ros,

      Looks like you have some great responses here for the issue you reported, but I thought I'd add a couple of enhancements to your code that I see might help out. You will hit some issues with your code, as the for statement will end up filling the entire array with the same email.

      You're also using both chain and SQL to access the same file, when we can be much more efficient and use SQL with cursors to retrieve our full set of records. I do recommend a bit of googling on the use of SQL cursors if you've not been exposed to this yet, but essentially, the SQL statement is executed and the cursor allows us to get the next set of data/results - kind of like a setll/read to retrieve the next record in the file.

      However, in this instance I've used a "fetch next from c1 for 15 rows into variable" which means that I've saved myself having to loop 15 times to fill up my array and I am retrieving the entire 15 emails in one go. Just to note, you do not need to declare the cursor as a variable, and always make sure that after you open one, that you close it before you leave the program, or you'll have alllllll sorts of fun results. In this example, I have kept the display to the number of records in your array (15), however, if you wanted to change this so you looped until all of the matches were added into the variable, you simply need to add the "fetch next" statement to get the next 15 records.

      I did want to leave you with a for loop example, so I've instead left only the emails in just an array (instead of in a Data Structure) and I am looping over each record and adding the separator text (' ;') to each record directly into the output variable.

      PHP Code:
              dcl-c MAXIMUM_ELEMENTS const(15);
               
      dcl-ds resultStructure dim(MAXIMUM_ELEMENTSqualified;
                 
      email varchar(50inz(*blanks);
               
      end-ds;  
               
      dcl-s numberOfElements zoned(4:0inz(MAXIMUM_ELEMENTS);  
               
      dcl-s i int(10:0inz;
               
      dcl-s acct varchar(20inz;
               
      dcl-s dspvar varchar(50inz;
               
      dcl-s totalRecordsFound int(10:0inz;

               
      exec SQL
                 SET OPTION COMMIT 
      = *NONE;

               
      //Prepare the search for all emails that are associated to an account like the account number
               
      acct '%' + %trim(acctp) + '%';
               
      exec SQL
                 
      declare c1 cursor for
                 
      select ADEMAD            
                 from   FOECADD
                 where  ADKEY1 like 
      :ACCT;

               
      exec SQL
                 open c1
      ;

               
      exec SQL
                 fetch next from c1
                 
      for :numberOfElements rows into :resultStructure;    

               
      // No records found
               
      if SQLCODE 100;
                 
      dspvar 'No records found';

               
      // An error was reported by the SQL
               
      elseif SQLSTATE >= '02000';
                 
      dspvar 'Error';

               
      // We have successfully retrieved our matching records.
               
      else;
                 
      totalRecordsFound SQLERRD(3);
                 for 
      1 to totalRecordsFound;
                   
      dspvar = %trim(dspvar) + resultStructure(i).email '; ';
                 endfor;
               endif;

               
      exec SQL
                 close c1
      ;

               
      dsply dspvar;

               *
      inlr = *on;
               return; 
      Additionally, I noticed that you have been going through the process of declaring initial values for your variables when starting new programs, however, there is a built in feature on the variable declaration of "inz" that will do this for you each time the program is called. If you just declare "inz" then the default value will be used (ie, blank for char, 0 for numerics etc), but you can actually put in a literal or constant value to explicitly declare the value for each variable too. I've put in a couple of examples to show you what I mean with this code.

      Anyway, don't feel like you have to use the method I've used above. There are many ways to skin a goat, and I thought this way may not be in use at your current shop and it is very useful. Seems you are picking up the concepts quickly, but feel free to reach out if you have any questions ((Also, please let me know if there's an issue with the code as I didn't have a chance to execute it before posting - won't have access to my machine until tomorrow)).

      Comment


      • Tho_Ros
        Tho_Ros commented
        Editing a comment
        Dude thank you so much for taking the time to explain all of this to me. Using SQL like this at my shop is not normal however IT MAKES SO MUCH MORE SENSE (at least to me). Explanations like this are invaluable, especially to a newbie like myself who stumbles on even the most simple issues. I will most definitely be reaching out in the future. Again, thanks a ton for your time @Ghost, I owe ya one!

      • Ghost +
        Ghost + commented
        Editing a comment
        SQL is a really neat tool to use but it does take a bit of figuring out (at least, I remember having troubles learning embedded SQL).

        And I'm happy I could be of some help - my virtual door is always open

    • #5
      I just noticed that your code has a DOU %EOF() loop but inside you do a CHAIN. That should be a DOU NOT %FOUND().

      Comment


      • jtaylor___
        jtaylor___ commented
        Editing a comment
        Well not exactly. It looks like the CHAIN always uses the same lookup values, so it will always return the same result. Anyway, don't mix %EOF and %FOUND.
    Working...
    X