SAP BOX

'26/11/12 UPDATE

ABAP / FIELD NOTES

I replaced READ TABLE with itab[ ]. Why does a missing row now end in a dump?

READ TABLE leaves sy-subrc 4 on a miss and carries on, but the same read written as an itab[ ] table expression raises CX_SY_ITAB_LINE_NOT_FOUND and, if nothing catches it, ends in a dump. Using a fictional example, this note shows what the old sy-subrc and sy-tabix checks now look at and how to choose between catching the exception, OPTIONAL/DEFAULT, line_exists and ASSIGN.

When you clean up an old program and turn READ TABLE ... INTO and the IF sy-subrc below it into the single line ls_price = lt_prices[ item = lv_item ]., the program can end in a dump the moment it looks for an item that is not there. Leaving the IF sy-subrc <> 0. check below it in place does not help. After reading this note you will be able to tell how READ TABLE and a table expression each report "not found", and at every place you convert, pick one of four ways (catching the exception, a default value, an existence check or ASSIGN) so that the shorter code adds no new dumps.

The example uses the fictional standard table lt_prices (columns item and price) and the fictional program ZDEMO_ITAB_EXPR. The table holds three items, A-100, A-200 and A-300, and we look for the missing item A-250. The code was not run on a real system. The behaviour described is what the ABAP Keyword Documentation, release 7.58, says about table expressions and the pages linked from it.

READ TABLE ends normally even when nothing is found

READ TABLE reads one line and sets the system fields sy-subrc and sy-tabix. When the line is found, sy-subrc is 0 and sy-tabix is that line's number in the table index used; with a hash key, sy-tabix is 0. When it is not found, sy-subrc is 4. If a binary search reached the end of the table, it is 8, and sy-tabix is the number of lines plus 1. After a miss without a binary search, sy-tabix is undefined. The value 2, which appears only with COMPARING, is a variant of "found".

The point is that the statement ends normally even on a miss. The work area ls_price stays unchanged or initial, and the program moves on to the next line. That is why old code puts IF sy-subrc <> 0. right after READ TABLE and handles "not found" inside it. The pattern of changing the found line and writing it back with MODIFY ... INDEX sy-tabix relies on the same system fields; it also appears in the documentation's READ TABLE example.

SAP GUI ABAP Editor. Old code reads the missing item A-250 from the fictional standard table lt_prices with READ TABLE INTO ls_price WITH KEY, checks IF sy-subrc <> 0 and copies sy-tabix into lv_row. New code replaces the read with ls_price = lt_prices[ item = lv_item ] and keeps the same sy-subrc check and sy-tabix assignment. Four numbered markers.
An example editor view with READ TABLE and the table expression meant to replace it. ZDEMO_ITAB_EXPR and the item values are fictional; the code was not run.

Read the numbers on the screen in order and you have the outline of this note. Number 1 is where READ TABLE leaves sy-subrc and sy-tabix, number 2 the same read written as a table expression, number 3 the old sy-subrc check left in place, and number 4 the line that copies sy-tabix.

A table expression raises an exception when nothing is found

A table expression itab[ ... ] finds one line by the condition in the square brackets and uses it as an operand on the spot. The documentation says each table expression can be viewed as a short form of a variant of READ TABLE, but the behaviour on a miss differs. If the specified line is not found, the catchable exception CX_SY_ITAB_LINE_NOT_FOUND is raised in all operand positions. That holds for a key condition such as item = lv_item and for a line number such as lt_prices[ 5 ]. If no TRY ... CATCH catches it, the program ends with runtime error ITAB_LINE_NOT_FOUND, the dump you see in ST22.

The documentation names four cases without the exception: a default value given while the result is defined with VALUE or REF; use in the ASSIGN statement (sy-subrc is 4); use inside the predicate function line_exists (false); and use inside the table function line_index (0). Outside these four places, itab[ ] reports "not found" only through the exception.

Example SAP GUI ST22 runtime error screen showing runtime error ITAB_LINE_NOT_FOUND, exception CX_SY_ITAB_LINE_NOT_FOUND, the fictional program ZDEMO_ITAB_EXPR and line 28. The error analysis says the exception was raised and not caught. In the source code extract, line 28 ls_price = lt_prices[ item = lv_item ] is marked, with the IF sy-subrc <> 0 check below it.
An example of the dump opened in ST22. The program, line number and values are fictional; this is not a dump from a real system.

In the example screen, the line the dump points to (number 3) is line 28, the table expression, and the IF sy-subrc right below it never ran. The exception class has attributes that show the line number or key of the failed access. The documentation also notes that when a statement contains several table expressions that read by line number, you cannot tell which one failed.

What the old sy-subrc and sy-tabix checks now look at

If you convert READ TABLE to a table expression and keep the checks after it, those checks no longer mean what they did. Start with sy-tabix. The documentation says that, unlike READ TABLE, a table expression does not change sy-tabix, except when used in ASSIGN. So lv_row = sy-tabix. at number 4 receives the value from whichever earlier statement last changed sy-tabix. Inside LOOP AT, that may be the current line number the loop set. Using it in MODIFY lt_prices FROM ls_price INDEX sy-tabix changes the line with that number, which has nothing to do with the line just read.

Then sy-subrc. ASSIGN is the only place where the documentation defines sy-subrc for a table expression. In ordinary operand positions the exception comes first when the line is missing, so the IF sy-subrc <> 0. block at number 3 never gets a chance to handle "not found". When the line exists, nothing in the documentation says the value that IF reads was set by this table expression. The old check stays behind as a line that neither prevents the dump nor confirms the result.

SAP NOTES

Same read, different system fields

Looking up the missing item A-250 in the fictional standard table lt_prices

  • READ TABLE … INTO ls_price

    sy-subrc = 4

    • Found: sy-subrc 0, sy-tabix = line number
    • Not found: sy-subrc 4, no exception
    • 8 when a binary search reaches the end
    • ls_price stays unchanged or initial
  • ls_price = lt_prices[ … ]

    CX_SY_ITAB_LINE_NOT_FOUND

    • Not found: this exception is raised
    • The IF sy-subrc on the next line is never reached
    • sy-tabix is not changed
    • What remains is an earlier statement's value
  • ASSIGN lt_prices[ … ] TO <fs>

    sy-subrc = 4

    • Not found: sy-subrc 4, no exception
    • 8 when a binary search on a sorted table reaches the end
    • sy-tabix is set as by READ TABLE
    • <fs> keeps its previous state: test sy-subrc

ASSIGN is the only place where the docs define sy-subrc for a table expression. Inside a LOOP, the sy-tabix left over may be the loop's current line number. Names and values are fictional.

READ TABLE reports a miss through sy-subrc; a table expression reports it with an exception. Only in ASSIGN does a table expression fill the system fields the way READ TABLE does.

Choose by what "not found" means

The documentation offers four ways, and which one fits depends on what "not found" means in that piece of code.

  • If the line should normally exist, use the table expression where you need it and catch the exception with TRY ... CATCH cx_sy_itab_line_not_found. If a missing line is a data error, the CATCH block logs a message or stops processing. The line_exists documentation suggests this approach when the line usually exists.
  • If an initial or default value is good enough, write VALUE #( lt_prices[ item = lv_item ] OPTIONAL ). When the line is missing, the result is an initial line instead of an exception. Writing DEFAULT VALUE #( item = lv_item ) in place of OPTIONAL makes a value you choose the result. A default value can be added only when the result is defined with VALUE or REF. There is one more difference: READ TABLE could leave the work area as it was on a miss, while here the result is set every time.
  • If you only need to know whether the line exists, write IF line_exists( lt_prices[ item = lv_item ] ). A miss gives false and no exception. If you need the line number, line_index( ) returns the number of the line found, or 0. line_exists does not fill sy-tabix. The documentation says not to check with line_exists and then read the same line again, because that runs the same search twice.
  • To read and branch at once, write ASSIGN lt_prices[ item = lv_item ] TO FIELD-SYMBOL(<ls_price>). followed by IF sy-subrc = 0. The documentation treats this as another form of READ TABLE ... ASSIGNING. On a miss there is no exception and sy-subrc is 4 (8 when a binary search on a sorted table reaches the end), and sy-tabix is set the same way as by READ TABLE. You can change the line directly through the field symbol, so MODIFY ... INDEX sy-tabix is not needed. On a miss the field symbol keeps its previous state, so test sy-subrc, not IS ASSIGNED.

SAP NOTES

What should 'not found' mean here?

Four documented ways, shown on the fictional table lt_prices

  • The line should exist

    CATCH cx_sy_itab_line_not_found

    • Inside TRY, use lt_prices[ … ] directly
    • Handle a miss in the CATCH block
    • Uncaught, it ends in ITAB_LINE_NOT_FOUND
  • An initial or default value will do

    VALUE #( lt_prices[ … ] OPTIONAL )

    • OPTIONAL: an initial line
    • DEFAULT def: a value you choose
    • Only with VALUE or REF
    • Since 7.40 SP08
  • Only existence matters

    line_exists( lt_prices[ … ] )

    • Not found: false, no exception
    • Line number: line_index( ), 0 if missing
    • Do not read the same line again afterwards
  • Read and branch at once

    ASSIGN lt_prices[ … ] TO <fs>

    • Not found: sy-subrc 4, no exception
    • sy-tabix is set as by READ TABLE
    • Test sy-subrc, not IS ASSIGNED

Checking with line_exists and then reading the same line searches twice. From 7.57, ASSIGN with ELSE UNASSIGN can also be tested with IS ASSIGNED. The code is a fictional example.

Decide whether a miss is a bug, a case for a default, or a branch, and one of the four ways follows.

Releases and BINARY SEARCH

According to the release notes, table expressions, line_exists and line_index arrived in 7.40 SP02, and the default values OPTIONAL and DEFAULT were added in 7.40 SP08. On a system with a support package in between, table expressions work but default values do not, so use TRY ... CATCH or ASSIGN. ELSE UNASSIGN, which leaves the field symbol unassigned on a miss, became available for ASSIGN with a table expression in 7.57; with it, testing IS ASSIGNED is also fine.

When converting a READ TABLE with BINARY SEARCH, check one more thing. A table expression with a free key has no way to force a binary search. If you need one, declare a sorted secondary key and name it with KEY in the table expression. According to the documentation, reads through a sorted key use a binary search.

What to decide before you shorten

Converting READ TABLE to a table expression is not a syntax cleanup; it changes how "not found" is handled. At every place you convert, find the sy-subrc and sy-tabix checks just below and answer one question first: if this line is missing, is it a bug (catch the exception), is a default fine (OPTIONAL/DEFAULT), or must the code branch right away (ASSIGN and sy-subrc)? Once the answer is clear, delete the old check or rewrite it for that way.

THE END
RSS feedBack to the library
COMMENTS BOX

Add your perspective.

Share a question, another approach, or something you have tried.

Newest first

Checking sign-in…

Loading comments…