Tuesday, January 24, 2017

Don’t forget the *return code



When we are developing in 2E we often reach the 50 file limit if we are generating for RPG.  RPG ILE (RP4) is more resilient as it allows more files to be opened.  I’ve preached before regarding proper function construction. 

Do you really need 50 or more files open for any given process? 
Can your function be better constructed or to be accurate………deconstructed?


What are the options?

1.       The easiest method to get over these limits is to change to RP4.
2.       The best is probably to re-architect your function properly and switch to RP4.
3.       The next best approach is to probably hive off a chunk of the processing into a new function to reduce the open files.
4.       The worst thing to do, is to externalise a single RTV in order to get under the limit.  As this can cause all sort of issues.
o   The 50 file limit will be breached on the next maintenance (most likely) leaving the potential for a string of EXT/RTV type functions.
o   If called in a loop or RTVOBJ then an external (if not set to Close Down ‘No’) may impact performance.

Another item to add the list of don’ts (above) is the preservation of the *Return Code.  An Execute External Function does not automatically return a *Return Code.  By default, standard processing is to pass back *Normal or an empty *Return Code.

If you are relying on a *Return Code for your now externalised RTVOBJ it will always come back as empty (*Normal)


unless you explicitly pass the return code back with the *EXIT PROGRAM built in function.

 
Another trap for young players but something that catches even seasoned developers out.  Had the RP4, re-architect or block of code to EEF option been taken the issue was likely to not have manifested.  

Good programming practice is to always be conscious of the *return code and more importantly (where required) test for it.
 
Thanks for reading.
Lee.

Tuesday, January 17, 2017

Condition of the timestamp field type

 Hi,

Recently we had a small issue in our office with a program not providing the expected results when a case statement was created to see if the timestamp was entered or not.

Here is a sample program that I mocked up in another model to demonstrate the point.

The example below is a simple EEF (Execute External Function) that compares a couple of timestamp fields.  The first LCL.Lee's Timestamp is initialised with a value of JOB.*System timestamp.  In the second part field LCL.Lee's Timestamp 2 is left uninitialised so this will be NULL so in synon 2E terms that means '0001-01-01-00.00.00.000000'.


As one field is initialised and the other isn't you expect that the code to be performed will generate two messages in the joblog and these would say 'Timestamp is entered' and 'Timestamp not entered".

However, as you can see from below the messages are not as expected.



Let us take a little look as to the potential reasons why this has happened. First of all let's take a look at the generated code (RPG) for the comparison conditions.



As you can see these are pretty standard apart from the fact that they are comparing a CONSTANT value rather than a field or a hardcoded value like 'A' which you often see for status fields etc.


In our case we created a condition called 'Entered' for the timestamp field.  We left it blank (upon creation) and 2E conveniently defaulted it to the NULL value '0001-01-01-00.00.00.00000'

Now, we know that 2E generates an array for the constants used by a program and references them in the code.  The array is 25 long(see above) and the value(s) have been correctly placed in the program source as below.


The condition value is stored as a reference via file YCNDDTARFP.  You will see that this condition references to another surrogate within 2E.


The clue is given in the source code snippet above where it refers to long constants.  Taking a look at the file YCONDTARFP for 1003038 we see the value that is used by the source.



 You can also see that this is 25 long in the file and as per the array declaration above.


Herein lies the issue....  The code is looking to compare the timestamp value vs a constant value.  The timestamp field is 26 long and has 6 decimal places of precision for the millisecond element.  If we place the program in debug and interrogate the value in LCL.Lee's Timestamp 2 we see that our field as below



  But, this is being compared to below.  These two are NOT the same.....


Now we understand the problem we need to consider how we can fix the issue.  Fortunately there are several workarounds for this...

  1. Move the value (LCL.Lee's Timestamp 2) into a shorter field 25 long (truncating the last 0).  We can then compare a 25 long field with a 25 long constant.  As we are really only ever interested in EQ, NE, GT or LT comparison operators this will be okay.
  2. We can modify the source (highlighted above) to set the constant array to 25 and add a 0 to the CN value.  But, we use a code generator so this is only recommended if you use source modifier programs and the pre-processor.
  3. Our chosen option was to NOT compare to a condition for 'Entered' or 'Not Entered' etc and instead compare to an empty field.  We created a NULL Timestamp field and referred to its LCL context for a comparison (LCL so that it is always initialised within it function bounds) and not compromised like the WRK context.
The code below is a simple example of how to implement option 3.



 The results are now as expected.


I've raised the issue with CA and expect a response soon.  Perhaps it is fixed, or on a list.  But considering it will mean a generator change and a file change for YCONDTARFP the workarounds above may be a better option so I hope that you all find this useful.

Thanks for reading.
Lee.

EDIT - I have heard from CA and the workaround option is the current recommend method. I agree with them that the scope and size of the change is quite high. Pick one of the three options above that works for your shop.  Happy to hear if there are other options.  LEe. 23/01/2017.