Ask Reuben

Initializer Rules

Why does sometimes a date variable contain NULL and other times it contains 31st December 1899?

Why does sometimes an integer variable contain NULL and other times it contains 0?

In my role I am frequently creating small examples and it used to annoy me that sometimes a date example would have a DATEEDIT widget with a value that defaulted to 31/12/1899 and sometimes it wouldn’t.  I had got into the habit of setting the date to TODAY so that when I opened the calendar widget I did not have to scroll through many years to enter a relevant date.  A recent forum post (thank you Benjamin G) sent me off down a rabbit hole that helped to explain why the date variable had the value it did and why sometimes it did not have that value.

The relevant documentation pages were this one on Variable default values and this section on Default values when adding new elements in Dynamic Arrays.

Essentially Informix-4gl had the behaviour that the underlying memory for variables were filled with 0’s.  As a result, numeric types such as INTEGER, SMALLINT but not DECIMAL would have an initial value of 0, and DATE would have an initial value of 31/12/1899 (number of days since 1/1/1900 -1).

When we added DYNAMIC ARRAY in one of the first iteration of Genero, as the memory for Dynamic Arrays was allocated on demand, this filling of memory variables with 0 did not occur and hence the initial values for these same data types would be NULL.

You can see this with the following example …

MAIN
    TYPE recType RECORD
        s STRING,
        i INTEGER,
        d DATE
    END RECORD

    DEFINE rec recType
    DEFINE s_arr ARRAY[10] OF recType
    DEFINE d_arr DYNAMIC ARRAY OF recType

    DISPLAY "*** Record ***"
    DISPLAY "String=",rec.s
    DISPLAY "Integer=", rec.i
    DISPLAY "Date=", rec.d

    DISPLAY "*** Static Array ***"
    DISPLAY "String=", s_arr[1].s
    DISPLAY "Integer=",s_arr[1].i
    DISPLAY "Date=", s_arr[1].d

    DISPLAY "*** Dynamic Array ***"
    DISPLAY "String=", d_arr[1].s
    DISPLAY "Integer=", d_arr[1].i
    DISPLAY "Date=", d_arr[1].d  END MAIN

… which produces the following output …

*** Record ***
String=
Integer= 0
Date=31/12/1899
*** Static Array ***
String=
Integer= 0
Date=31/12/1899
*** Dynamic Array ***
String=
Integer= 
Date=

Note how for Record and Static Array, the Integer and Date elements have a value whilst for a Dynamic Array the value is blank (null).

These decisions were made in the early 2000’s or earlier.  For some understanding to the DYNAMIC ARRAY behaviour, ask yourself what would you expect intermediate rows to be populated with if I had added row 100 instead of row 1.  Rows 1 to 99 would need to have its memory set to 0 but these rows have not had their memory allocated yet.  Also consider the case of a temporary row in an INPUT ARRAY, is a row touched if the user accepts the default value of 0?

The question might then be what can you do to avoid this inconsistency.  There is no fglprofile flag to favour one behaviour over the other.

What you can do to avoid 0 and 31/12/1899 is to use the =NULL variable initializer.   Modify the above definitions to be

DEFINE rec recType = NULL
DEFINE s_arr ARRAY[10] OF recType= NULL

and you will see the values are now NULL instead of 0 and 31/12/1899 for the integer and date element.  You could use

INITIALIZE rec.* TO NULL

but for an array you would have to have a FOR loop for every row, hence I lean towards the newer = NULL initializer syntax.

To go the other way for a dynamic array and set the same initial values as a record and static array you would have to populate the new row with an empty record when you create the new dynamic array element …

LET d_arr[1].* = rec.*

… you would also have to do that for any intermediate rows that have not been similar initialized so I would be very surprised if anyone went down this road.

As part of that forum discussion, we did review the documentation page .  It might not be immediately obvious that you can use =NULL as an initializer for a record  or array but you can.