Thursday, July 31, 2014

Mistakes to watch out for with NULL in lists

The main thing to watch out for is the usage of the <1,-1> code for accessing/updating a list. When you insert a NULL using <1,-1>, it doesn’t really do anything.

In this case, we had three lists that were being used like an association, trying to be retrieve data from a record:

*Stores each field we care about from STUDENT.ACAD.CRED record in lists. The intention is that these lists are associated.
FOR_EACH REFERENCED SECONDARY PST.STUDENT.ACAD.CRED
  XL.STC.CMPL.CRED<1,-1> = V.STC.CMPL.CRED
  XL.STC.STATUS<1,-1> = VL.STC.STATUS<1,1>
  XL.STC.END.DATE<1,-1> = V.STC.END.DATE
END_EACH PST.STUDENT.ACAD.CRED

The issue arises when you consider how assigning NULLs work in UniData. Since a NULL, in Envision, is not actually a character but an empty string, assigning a null to a list doesn’t actually update the list, so when we hit a NULL value in the assignment the positions of the three lists went wrong. If our starting data was like this:

Record #1:
V.STC.CMPL.CRED = NULL
VL.STC.STATUS<1,1> = ‘N’
V.STC.END.DATE = ‘7/11/2014’

Record #2:
V.STC.CMPL.CRED = ‘3.00’
VL.STC.STATUS<1,1> = ‘N’
V.STC.END.DATE = ‘7/12/2014’

Our ending lists would look like this, based on our code above:

STC.CMPL.CRED
STC.STATUS
STC.END.DATE
1
3.00
N
7/11/2014
2
NULL
N
7/12/2014

In record 1, V.STC.CMPL.CRED was NULL, so it actually did not create a delimiter in the XL.STC.CMPL.CRED in position 1; we merely set the tail of the list equal to NULL, so it didn’t actually do anything. The next record then sticks ‘3.00’ into the tail of the list, which is in position 1. Since position 2 is never set, our three lists are going to look like:

XL.STC.COMPLE.CRED = ‘3.00’
XL.STC.STATUS = ‘N’:@VM:’N’
XL.STC.END.DATE = ‘7/11/2014’:@VM:’ 7/12/2014’

Example of the issue using <1,-1>:

XL.LIST<1,-1> = ‘’
XL.LIST<1,-1> = ‘’
XL.LIST<1,-1> = ‘’
XL.LIST<1,-1> = ‘’
XL.LIST<1,-1> = ‘’
XL.LIST<1,-1> = ‘TEST’

In this case, XL.LIST is now equal to ‘TEST’ with no delimiters. In order to make this work, what you actually need to do is explicitely define the positions:

XL.LIST<1,1> = ‘’
XL.LIST<1,2> = ‘’
XL.LIST<1,3> = ‘’
XL.LIST<1,4> = ‘’
XL.LIST<1,5> = ‘’
XL.LIST<1,6> = ‘TEST’

Now XL.LIST is equal to ‘’:@VM:’’:@VM:’’:@VM:’’:@VM:’’:@VM:’TEST’ with @VM as the delimiter.

In order to fix the original code, I replaced

FOR_EACH REFERENCED SECONDARY PST.STUDENT.ACAD.CRED
  XL.STC.CMPL.CRED<1,-1> = V.STC.CMPL.CRED
  XL.STC.STATUS<1,-1> = VL.STC.STATUS<1,1>
  XL.STC.END.DATE<1,-1> = V.STC.END.DATE
END_EACH PST.STUDENT.ACAD.CRED

With

X.ACAD.COUNTER = ‘1’
FOR_EACH REFERENCED SECONDARY PST.STUDENT.ACAD.CRED
  XL.STC.CMPL.CRED<1,X.ACAD.COUNTER> = V.STC.CMPL.CRED
  XL.STC.STATUS<1,X.ACAD.COUNTER > = VL.STC.STATUS<1,1>
  XL.STC.END.DATE<1,X.ACAD.COUNTER> = V.STC.END.DATE
  X.ACAD.COUNTER = X.ACAD.COUNTER + 1

END_EACH PST.STUDENT.ACAD.CRED


* Cited from Trevyn Bowden.

Check to see if a date is already converted to internal format

IF NOT(NUM(A.DATE)) THEN
      * date is not in internal format, needs converting
      A.DATE = ICONV(A.DATE,X.SN.DATE2)

END

Useful for input validation of dates on things like subroutines and ELF

* from Trevyn Bowden.