When I wrote about using DEFAULT in SQL statements it made me think if there is an equivalent in RPG. The Clear operation code sprung to mind. CLEAR has been around for many releases, including in RPGIII. It initializes the value of a variable, field, data structure, array, record format, etc. to the data type's default value.
I use CLEAR extensively because…
- When I want to initialize a field, variable I do not have to know what its data type is
- I can clear a data structure in one statement, and the sub-fields within are initialized to their data type's default value
- I can clear an array in one statement
- I can clear all of the fields in a record format in one statement
- When reading the code another programmer can easily see that I am just initializing the field, variable, etc.
Initializing variables
In the very simple example, below, I came up with the most commonly used data types I use. I did not bother to include signed, integer, and unsigned integer variables as they all act the same as the packed variable.
01 dcl-s xChar char(10) inz('Test') ; 02 dcl-s xPacked packed(3) inz(123) ; 03 dcl-s xDate date inz(*sys) ; 04 dcl-s xTime time inz(*sys) ; 05 dcl-s xTimestamp timestamp inz(*sys) ; 06 dcl-s xInd ind inz('1') ; 07 clear xChar ; 08 clear xPacked ; 09 clear xDate ; 10 clear xTime ; 11 clear xTimestamp ; 12 clear xInd ; |
For those of you unfamiliar with the INZ(*SYS), used on lines 3 – 5, you can learn about it in the post Initializing variables with special values.
The Clear operation initializes the variables to be:
Data type | Value after Clear |
Alphanumeric | Blank |
Numeric (packed, signed, integer, unsigned integer) | Zero |
Date | d'0001-01-01' |
Time | t'00.00.00' |
Timestamp | z'0001-01-01-00.00.00.000000' |
Indicator | Off |
The handling of the date, time, and timestamp variables is different when I use the CLEAR when compared to using DEFAULT in a SQL statement. SQL's DEFAULT would have initialized those variables to the current date, time, and timestamp.
Initializing data structures
Initializing a data structure is one of the reasons I love the CLEAR.
01 dcl-ds xDs qualified ; 02 Char char(10) inz('ds test') ; 03 Packed packed(3) inz(987) ; 04 Date date inz(d'2016-12-25') ; 05 Time time inz(t'13.20.00') ; 06 Timestamp timestamp inz(*sys) ; 07 Ind ind inz('1') ; 08 end-ds ; 09 clear xDs ; 10 reset xDs ; |
Lines 1 - 8: I am initializing the sub-fields of my data structure with values at program initialization.
Line 9: With this one statement all of the sub-fields are initialized with their data type's default values:
> EVAL xds XDS.CHAR = ' ' XDS.PACKED = 000. XDS.DATE = '0001-01-01' XDS.TIME = '00.00.00' XDS.TIMESTAMP = '0001-01-01-00.00.00.000000' XDS.IND = '0' |
Line 10: I find that not many programmers are aware of the Reset operation code, RESET. This operation code will reset the values of a field or data structure to its value at program initialize. So after line 10 is executed the data structure now contains:
> EVAL xds XDS.CHAR = 'ds test ' XDS.PACKED = 987. XDS.DATE = '2016-12-25' XDS.TIME = '13.20.00' XDS.TIMESTAMP = '2016-06-23-12.46.29.092000' XDS.IND = '1' |
I am sure you all agree that is pretty cool, and useful, which is why I felt I had to mention the RESET.
What about other types of Data Structures:
- Data Structures arrays – I will cover them in the section about arrays below
- Multiple occurrence Data Structures
I have not used multiple occurrence Data Structures since I discovered Data Structure arrays, but there are many programs and programmers who still use them.
01 dcl-ds xMultiDs qualified occurs(2) ; 02 First char(1) ; 03 Second char(1) ; 04 end-ds ; 05 exsr LoadMultiDs ; 06 %occur(xMultiDs) = 1 ; 07 clear xMultiDs ; 08 %occur(xMultiDs) = 2 ; 09 exsr LoadMultiDs ; 10 %occur(xMultiDs) = 1 ; 11 clear *all xMultiDs ; 12 %occur(xMultiDs) = 2 ; //======================= 13 begsr LoadMultiDs ; 14 %occur(xMultiDs) = 1 ; 15 xMultiDs.First = '1' ; 16 xMultiDs.Second = '1' ; 17 %occur(xMultiDs) = 2 ; 18 xMultiDs.First = '2' ; 19 endsr ; |
Lines 1 – 4: This is my definition of my multiple occurrence Data Structure.
Line 5: Rather than repeat the code to load occurrences of the Data Structure I have placed the logic in a subroutine, lines 13 – 19.
Line 6: Get the first occurrence of the Data Structure.
Line 7: Perform the clear.
Line 8: Get the second occurrence. If I use debug I will see that the second occurrence still contains the data I loaded into it.
Lines 9 – 12: Are basically the same except…
Line 11: This time the clear has the *ALL keyword. This means that all occurrences are cleared, not just the current one.
Initializing arrays
Using CLEAR I can clear an entire array in one statement.
01 dcl-s xArray char(1) dim(5) ; 02 xArray(1) = '1' ; 03 xArray(2) = '2' ; 04 xArray(3) = '3' ; 05 xArray(4) = '4' ; 06 xArray(5) = '5' ; 07 clear xArray ; |
Even if the array is a Data Structure array.
01 dcl-ds xArrayDs qualified dim(2) ; 02 First char(1) ; 03 Second char(1) ; 04 end-ds ; 05 xArrayDs(1).First = '1' ; 06 xArrayDs(1).Second = '1' ; 07 xArrayDs(2).First = '2' ; 08 clear xArrayDs ; |
Initializing record format
I can use the clear to initialize an entire record format from a file, no matter if it is a physical, logical, display, or printer file. Below is a simple physical file I will be using in these examples. Notice how the field FDFT has a default value, and FNULL allows null and its default is also null.
A R TESTFILER A FCHAR 3A A FPACKED 3P 0 A FDATE L A FTIME T A FTINESTAMP Z A FDFT 1A DFT('A') A FNULL 1A ALWNULL A DFT(*NULL) A K FCHAR |
I have added a record to the file, the dash indicates that the field is null.
FCHAR FPACKED FDATE FTIME FTINESTAMP FDFT FNULL AAA 111 1995-02-21 05.21.13 1962-01-31-14.08.33.000000 A - |
In my first example I am going to read the file, clear the record format, and write the record to the file.
01 dcl-f TESTFILE usage(*input:*output) keyed ; 02 read TESTFILER ; 03 clear TESTFILER ; 04 write TESTFILER ; |
The file now has two records. As the second record was cleared it contains the data types's defaults, not the field defaults. Which is why the field FSDFT now contains a blank. FNULL remains unchanged as you cannot clear a null value.
FCHAR FPACKED FDATE FTIME FTINESTAMP FDFT FNULL AAA 111 1995-02-21 05.21.13 1962-01-31-14.08.33.000000 A - 0 0001-01-01 00.00.00 0001-01-01-00.00.00.000000 - |
Before my next example I changed the value of FNULL to set off the null indicator and to contain "N". If you are unfamiliar with null indicators in RPG you need to read Handling null in RPG.
In this example I read the file, I clear the record format and use the *NOKEY keyword, and then write the record back to the file.
02 read TESTFILER ; 03 clear *nokey TESTFILER ; 04 write TESTFILER ; |
The *KEY keyword "protects" the key field from being cleared, therefore, the records in my file now look like.
FCHAR FPACKED FDATE FTIME FTINESTAMP FDFT FNULL AAA 111 1995-02-21 05.21.13 1962-01-31-14.08.33.000000 A N AAA 0 0001-01-01 00.00.00 0001-01-01-00.00.00.000000 |
 
As you can see the CLEAR is a really versatile operation code that deserves to be included in your programs. It will save you a lot of extra lines of code to achieve the same result, and make it easier for others to understand.
You can learn more about the CLEAR operation code from the IBM website here.
This article was written for IBM i 7.2, and should work for earlier releases too.
You can override the default INZ value in the *INZSR so that when you RESET a variable it will actually go to the value after the *INZSR?
ReplyDeleteYou are correct. If I initialize a variable with a value in a DCL-S, then I change the value in the *INZSR, when I use the RESET operation the value it is changed to is the value I set it to in the *INZSR.
Delete... that's why it is important to understand the difference between CLEAR and RESET. In order of clean coding and readability, INZ values should always be set using the INZ keyword! It is a pain, IBM keeps supporting this stoneage stuff ...
DeleteVery Informative!!
ReplyDelete