ABAP Implicit Work Areas
This blog is intended for newer developers who might not have been exposed to the implicit work area concept in their initial training. Most of these implicit work area constructs are obsolete for general programming use and typically are forbidden syntax within ABAP Objects classes. All of this information is contained in the Online Help, but is sometimes obscured by newer syntax additions.
The ABAP Language has been around since 1983, and one of its interesting traits is that code written “back in the day” will still execute now. This backwards compatibility means we get newer, more powerful syntax elements because we don’t have to rewrite applications every time new syntax appears. But it also means there is some really old, odd-looking code still out there. We developers will run across some of these constructs when debugging. My intent is to shed some light on what these constructs are and what they do.
Way back in the 1980’s and early 1990’s, internal tables were declared using the OCCURS clause. A typical internal table definition looked like this:
DATA: BEGIN OF itab1 OCCURS 0, carrid TYPE sflight-carrid, connid TYPE sflight-connid, fldate TYPE sflight-fldate. DATA: END OF itab1.
The OCCURS clause was used to declare this structure as an internal table. The same definition without the OCCURS clause defines a structure. The number following the word OCCURS was used to declare the initial memory size of the internal table. The default value of zero was, in practice, used almost universally by ABAP developers.
The OCCURS clause also created a “HEADER LINE”, an implicit work area for the internal table. This work area created a structure in memory with the same name, row type and technical characteristics as the ITAB definition. Because of this, the ITAB was accessed without referencing a target structure:
LOOP AT itab1. WRITE: / itab1-carrid, itab1-connid, itab1-fldate. ENDLOOP.
Here, the LOOP AT is referencing each ITAB row in turn and placing that single row into the implicit work area. Since the OCCURS created the work area with the same name as the ITAB, the WRITE statements are referencing the work area fields, NOT the internal table fields. Needless to say, this can lead to confusion when we see this construct today!
Later on, ABAP was changed to allow us to reference structures when defining ITABs:
DATA: new_itab TYPE sflight OCCURS 0 WITH HEADER LINE.
This construct built an ITAB and the “WITH HEADER LINE” extension created an implicit work area.
Another place where we find an implicit work area is with SELECT-OPTIONS and RANGE tables.
The SELECT-OPTIONS keyword is still a vital part of ABAP and it manages a lot of things for us. It creates the familiar user interface that allows single or multiple selections, ranges, and wildcard searches to be used in SELECT statements and other conditionals (SELECT FROM <table name> WHERE <field name> IN <select-option name>).
It also creates an ITAB in memory. This ITAB contains four fields named SIGN, OPTION, LOW and HIGH:
In addition, the SELECT-OPTIONS keyword also builds an implicit work area for the ITAB:
Not only does the SELECT statement access this ITAB, we as developers can access this ITAB for our own purposes. To do this we need to distinguish between the body of the ITAB and the implicit work area.
In figure 2, note the debugger shows the table name SO_CARR is suffixed with brackets, versus Figure 3. The brackets indicate that we are looking at the body of the internal table; without brackets, we are looking at the implicit work area.
This is not unique to SELECT-OPTIONS; any of the above internal tables with an implicit work area uses brackets to reference the body of the ITAB and no brackets to reference a structure. An example is:
IF so_carr IS NOT INITIAL.
This would check to see if the body ITAB is empty or if it contains values. In this particular example, we are looking at the user interface SO_CARR, and we can determine whether or not the user entered any values in the SELECT-OPTION for this field. This would be helpful if we need to edit those values before we perform the SQL SELECT statement (to constrain the search for performance reasons, for example).
The obsolete RANGES keyword operates in the same manner, but does not create a user interface. Since there is no user interface, we need to add entries programmatically.
rg_date-sign = 'I'. rg_date-option = 'EQ'. rg_date-low = sy-datum. APPEND rg_date TO rg_date.
Note that we are APPENDing the implicit work area (no brackets) to the ITAB body (with brackets).
We could also get fancy and do something like this:
rg_date = 'IBT2019010120190228'. APPEND rg_date TO rg_date.
This would make the complete row entry in two commands.
The result of the two APPENDs would be:
Now we can use this ITAB in a conditional or an SQL statement.
One last implicit work area is the TABLES statement. The TABLES statement comes in three flavors, but only the first is still legitimate syntax
The first flavor is only for module pool programming and is the only legitimate use of TABLES in new code. Here, the TABLES statement is used as a communication path to pass data from the ABAP variables to the screen fields (this is an over-simplification; see the online help for details).
The second flavor uses the TABLES statement as a target work area for an SQL SELECT:
TABLES: sflight. SELECT * FROM sflight. WRITE: / sflight-carrid, sflight-connid, sflight-fldate. ENDSELECT.
This syntax is copying each row of the database table SFLIGHT into the implicit work area SFLIGHT in our program’s memory, then writing three fields from that implicit work area. Today, we would explicitly code a target in the SQL SELECT statement.
The third flavor is:
This allowed a developer to have two work areas for the same table. This was used often in update programs to hold the original values of a record being changed. A different use was “what-if” processing, where two different versions of a record could be held to compare before and after values. Today, we would create two identical structures with separate names.
I hope this sheds some light on an obscure (but important) point and that you will find it helpful the next time you are debugging some ancient code!