One of my colleagues was reading the post about initializing variables when defining them and asked a question that I had not considered when writing that post.
"What if I change the value in the variable," he asked, "can I change it back to its original value?"
Fortunately there is in RPG an operation code that will do this. The RESET operation resets the value of any variable or record format back to the value it was at the initialization phase of the program. It is not new operation, I have used it for as long as I have programmed on the AS400 - IBM i.
Let me give some examples of using it. These are going to be in fully free format RPG, but can easily be retrofitted back to the old versions of the language.
01 dcl-s Var1 char(10) inz('Hello') ; 02 Var1 = 'Goodbye' ; 03 clear Var1 ; 04 reset Var1 ; |
The above example is probably the simplest example I could create to demonstrate his operation code.
Line 1: When I define the variable I am initializing it with a value, the word "Hello".
Line 2: I change the value in the variable to "Goodbye".
Line 3: The CLEAR operation code clears the variable to blank, as this is a character field.
Line 4: When I use the RESET the value of the variable is restored to what it was at the initialization phase of the program. If I run the program in debug after this line has executed I can see the original value.
VAR1 = 'Hello ' |
Let me make this a bit more complicated.
01 dcl-s Var1 char(10) inz('Hello') ; 02 Var1 = 'Goodbye' ; 03 clear Var1 ; 04 reset Var1 ; 05 begsr *inzsr ; 06 Var1 = 'Hi!' ; 07 endsr ; |
This is the same program as my previous example, just with a subroutine added, lines 5 – 7. The initialization subroutine, *INZSR, is called automatically by the program at initialization time. There is never a EXSR for it. In this example I am moving the value "Hi!" to the variable.
If I run the program in debug after line 4 has executed I see:
VAR1 = 'Hi! ' |
The value I initialized the variable with, in the definition, has been replaced by the value from the initialization subroutine, because the subroutine happens later in the program's initialization routines.
I can use the RESET with arrays and single occurrence data structures, if I have not initialized them with a value the reset will work like the CLEAR operation.
There is a parameter I must use to reset a multiple occurrence data structure in one statement:
01 dcl-ds MultiDs qualified occurs(2) ; 02 Subfield1 char(2) ; 03 subfield2 packed(2) inz(99) ; 04 end-ds ; 05 %occur(MultiDs) = 1 ; 06 MultiDs.Subfield1 = 'AA' ; 07 MultiDs.Subfield2 = 1 ; 08 %occur(MultiDs) = 2 ; 09 MultiDs.Subfield1 = 'BB' ; 10 MultiDs.Subfield2 = 2 ; 11 reset *all MultiDs ; 12 %occur(MultiDs) = 1 ; 13 %occur(MultiDs) = 2 ; |
Lines 1 – 4: This is definition of my multiple occurrence data structure. Note this has to have an OCCURS keyword on line 1 to indicate this data structure has two occurrences. Subfield2 is initialized with the value 99.
Lines 5: In free format the OCCUR operation code has been replaced by the %OCCUR built in function. On this line I get the first occurrence of the data structure.
Lines 6 and 7: I move values to the subfields.
if I am running debug and I look at the data structure before line 8 I see:
EVAL multids MULTIDS.SUBFIELD1 = 'AA' MULTIDS.SUBFIELD2 = 01. |
Line 8: I get the second occurrence of the data structure.
Lines 9 and 10: I move values to the subfields.
As I am running debug if I look at the data structure before line 11 I see:
EVAL multids MULTIDS.SUBFIELD1 = 'BB' MULTIDS.SUBFIELD2 = 02. |
Line 11: This RESET has the *ALL. Without the *ALL the reset would only reinitialize the current occurrence of the data structure. The *ALL allows me to reinitialize all occurrences.
Line 12: I get the first occurrence again. After this line has executed as I am in debug when I look at the data structure is has been reinitialized.
EVAL multids MULTIDS.SUBFIELD1 = ' ' MULTIDS.SUBFIELD2 = 99. |
Line 13: I get the second occurrence and look at it in debug it contains the same as the first occurrence.
Note: I no longer use multi occurrence data structures as I believe that data structure arrays are easier to use and for other programmers to understand when working with my code.
I can all use RESET with files to reset fields back to the values they were at program initialization. In the following example I will be using a file called TESTFILE.
01 A R TESTFILER 02 A F1 3A 03 A F2 3A 04 A F3 3A 05 A USER 10A 06 A WRITTEN Z 07 A K F1 |
Line 2 and 7: F1 is the file's only key field.
Line 3 and 4: F2 and F3 are just other alphanumeric fields.
Line 5: USER will be the user id of the last person who wrote or updated the record.
Line 6: WRITTEN is the timestamp of when the record was written or updated.
Now I need to define this file in my RPG program.
01 dcl-s FixedUser char(10) inz(*user) ; 02 dcl-f TESTFILE usage(*output) ; 13 begsr *inzsr ; 14 F3 = 'ABC' 15 USER = FixedUser ; 16 endsr ; |
Line 1: I have initialized the variable FixedUser with the user's profile.
Line 2: I have defined my file TESTFILE as output only.
Lines 13 – 16: This is another initialization subroutine. I am moving values into the file's fields F3 and USER.
Now for the first write to the file:
03 F1 = 'Y' ; 04 F2 = 'Z' ; 05 WRITTEN = %timestamp() ; 06 write TESTFILER ; |
That is all straightforward and results in the following record in TESTFILE:
F1 F2 F3 USER WRITTEN Y Z ABC SIMONH 2017-08-08-20.04.08.294000 |
Before the next write I am going to reset the record format.
07 reset *all TESTFILER ; 08 write TESTFILER ; |
Then only two fields to contain values are the two that were used in the initialization subroutine.
F1 F2 F3 USER WRITTEN ABC SIMONH 0001-01-01-00.00.00.000000 |
There is another value I can use in the reset operation for files only. The *NOKEY preserves the file's key fields from being reset. For example:
09 F1 = 'A' ; 10 F2 = 'B' ; 11 reset *nokey *all TESTFILER ; 12 write TESTFILER ; |
Line 11: *NOKEY must come before the *ALL for the program to compile.
After this write I can see that the file's key field, F1 was preserved as well as the fields that were set in the *INZSR.
F1 F2 F3 USER WRITTEN A ABC SIMONH 0001-01-01-00.00.00.000000 |
If I am initializing variables when defining them or in the initialization subroutine, *INZSR, the reset operation code becomes extremely useful in restoring the original values, allowing me to use them again. Rather than with a clear operation where I would have to move the default value back to the variable after it has been cleared.
You can learn more about the RESET operation code from the IBM website here.
This article was written for IBM i 7.3, and should work for earlier releases too.
This is a great, comprehensive coverage. Thank you for putting t6his great resource together with simple to follow examples.
ReplyDeleteAnother great publication. Thanks!
ReplyDeleteOne remark:
In line 14, you probably changed F3 field (and not F2)
13 begsr *inzsr ;
14 F2 = 'ABC'
15 USER = FixedUser ;
16 endsr ;
Good catch for finding that, and thank you for letting me know. I have made the correction.
DeleteI have used MODS to do an SQL multi-fetch. Is there a way to use an array instead?
ReplyDeleteUsing a data structure array is so much easier/simpler than using a multiple occurrence data structure.
DeleteFor more information see :
SQL blocking fetches, getting more than one row at a time
Creating a program to show jobs in message wait