You are on page 1of 66

ABAP FAQs

This Document Provides Answers to Frequently Asked Questions About:

 Starting the debugger in a modal screen

 Retrieving field names dynamically

 Restricting the selection screen

 Calling an external program

 Using a variable from a calling program without passing it in parameter

 List of internal tables while debugging

 Showing a progress bar in ABAP

 Manipulating Timestamps

 Generating your own standard F4 help

 Creating an ALV Grid in 3 lines

 Modifying the F4 standard calendar

 Saving an internal table in Microsoft Excel format

 Generating a number range automatically

 Defining a local type dynamically

 Using macros

 Using dynamic SELECT statements

 Using JavaScript in ABAP

 Saving an internal table in Microsoft Excel format

 Saving an internal table in Microsoft Excel format

 Managing persistent objects with object services

 Debugging ABAP programs from JAVA applications

 Defining a default variant


 Creating Activable Breakpoints and Assertions

 Using Shared Objects

 Defining dynamic orders in an ABAP sort

 Inserting charts

 Knowing when to use SELECT SINGLE or SELECT ... UP TO 1 ROWS

 Working around the limitations of a range in SELECT...WHERE...IN

 Calling function modules dynamically

 Downloading ABAP code to your local PC

Starting the debugger in a modal screen

Usually in ABAP, when you want to start the debugger from a certain point, you
just have to write "/H" in the

command window. But if you are in a modal screen or in a message display, you
cannot write the traditional

"/H", here is how to do it :

1. Open notepad and paste these lines in a document : [FUNCTION]


Command=/H Title=Debugger Type=SystemCommand
2. Save this document anywhere on your local pc. ex: c:\breakpoint.txt
3. Drag&drop this file from the explorer to your ABAP modal screen.... et
voila !!

Back to top

Retrieving field names dynamically

Sometimes you may have only a table name and want to retrieve the name of
each field of the corresponding table. For example, when you want to use ASSIGN
COMPONENT fieldname OF TABLE table.

An ABAPer's first reaction is to read the standard ABAP basis tables DD02L,
DD03L, etc

This way of reading fields is very slow. Use methods from the class
CL_ABAP_TYPEDESCR instead.
Example:

data: descr_struc_ref TYPE REF TO cl_abap_structdescr.

descr_struc_ref ?= cl_abap_typedescr=>describe_by_name('SFLIGHT' ).

Here is the result of descr_struct_ref after the execution of this piece of code

ABSOLUTE_NAME C 200 \TYPE=SFLIGHT

TYPE_KIND C 1 u

LENGTH I 4 80

DECIMALS I 4 0

KIND C 1 S

STRUCT_KIND C 1 F

COMPONENTS h 8 Table[14x40]

HAS_INCLUDE C 1

The table COMPONENTS is filled with :

LENGTH DECIMALS TYPE_KIND NAME

3 | 0 |C |MANDT

3 | 0 |C |CARRID

4 | 0 |N |CONNID

8 | 0 |D |FLDATE

etc.

You have the fields name and a lot more information about the table. This class
can also handle structure, table type, etc.

Note that this method is very fast because it uses the database layer directly
thanks to SYSTEM CALLs.

To have a look at the class, use transaction SE24.


Back to top

Restricting the selection screen

When you create a select-option for an input to your program, for each field, the
default selection screen looks like this:

And the default possible selections are:


but sometime you don't want to give the user the possibility to select a range,
select values "greater than", etc.

Here is a small example on how to do it:

REPORT ZDANY_RESTRICT_SELECTION.

* Include type pool SSCR

TYPE-POOLS sscr.

TABLES : sflight.

* defining the selection-screen

select-options :

s_carrid for sflight-carrid,

s_connid for sflight-connid.


* Define the object to be passed to the RESTRICTION parameter

DATA restrict TYPE sscr_restrict.

* Auxiliary objects for filling RESTRICT

DATA : optlist TYPE sscr_opt_list,

ass type sscr_ass.

INITIALIZATION.

* Restricting the carrid selection to only EQ and 'BT'.

optlist-name = 'OBJECTKEY1'.

optlist-options-eq = 'X'.

optlist-options-bt = 'X'.

APPEND optlist TO restrict-opt_list_tab.

ass-kind = 'S'.

ass-name = 'S_carrid'.

ass-sg_main = 'I'.

ass-sg_addy = space.

ass-op_main = 'OBJECTKEY1'.

APPEND ass TO restrict-ass_tab.

* Restricting the connid selection to CP, GE, LT, NE.

optlist-name = 'OBJECTKEY2'.

optlist-options-cp = 'X'.

optlist-options-ge = 'X'.

optlist-options-lt = 'X'.
optlist-options-ne = 'X'.

APPEND optlist TO restrict-opt_list_tab.

ass-kind = 'S'.

ass-name = 'S_connid'.

ass-sg_main = 'I'.

ass-sg_addy = space.

ass-op_main = 'OBJECTKEY2'.

APPEND ass TO restrict-ass_tab.

CALL FUNCTION 'SELECT_OPTIONS_RESTRICT'

EXPORTING

restriction = restrict

EXCEPTIONS

TOO_LATE = 1

REPEATED = 2

SELOPT_WITHOUT_OPTIONS = 3

SELOPT_WITHOUT_SIGNS = 4

INVALID_SIGN = 5

EMPTY_OPTION_LIST = 6

INVALID_KIND = 7

REPEATED_KIND_A = 8

OTHERS = 9.

IF sy-subrc <> 0.

MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.


ENDIF.

when you execute this piece of code, you will notice that for carrid, the selection
screen is now restricted :
Back to top

Calling an external program

You want to call a program on your PC from an ABAP program ? try this function :

*This example calls the dos prompt DATA:

i_returncode TYPE i.

CALL FUNCTION 'GUI_EXEC'

EXPORTING

command = 'CMD' <<==-- CMD calls the DOS prompt but you can put any program
here

* PARAMETER = 'test'

IMPORTING
returncode = i_returncode.

there is a lot of other functions to communicate with the PC like:

GUI_CREATE_DIRECTORY GUI_DELETE_FILE GUI_DOWNLOAD

GUI_EXEC

GUI_FILE_SAVE_DIALOG

GUI_GET_DESKTOP_INFO

GUI_GET_FILE_INFO

etc...

Back to top

Using a variable from a calling program without passing it in parameter

Did you ever try to use a variable from another program without passing this
variable as a parameter. This is very useful when you CANNOT add the field as a
standard parameter. For example, when you want to use a variable in a BADI
which is not already passed as a parameter. Another good example is when you
create a correction in a function and you want to keep the installation of the OSS
note automatic (if you add parameters in a note, the user will have to install the
note manually).

Here is a short example on how to do this :

REPORT zdany_test_var_from_fm.

TABLES: spfli.

DATA dbcnt TYPE sy-dbcnt.

DATA: itab TYPE spfli_tab.

SELECT * FROM spfli INTO TABLE itab UP TO 2 ROWS.

dbcnt = sy-dbcnt.

CALL FUNCTION 'ZFUNCTION'.

FUNCTION zfunction.

* We want to use the DBCNT from the program ZDANY_TEST_VAR_FROM_FM

DATA: field(50).

FIELD-SYMBOLS: <dbcnt>.

field = '(ZDANY_TEST_VAR_FROM_FM)dbcnt'.

ASSIGN (field) TO <dbcnt>.

WRITE <dbcnt>.

* We want to use the internal table from the program


ZDANY_TEST_VAR_FROM_FM

DATA: itab TYPE spfli.

FIELD-SYMBOLS: <itab> TYPE spfli_tab.

field = '(ZDANY_TEST_VAR_FROM_FM)ITAB[]'.

ASSIGN (field) TO <itab>.

LOOP AT <itab> INTO itab.


WRITE: / itab-carrid, itab-connid.

ENDLOOP.

ENDFUNCTION

Back to top

List of internal tables while debugging

There can be times when you need a list of active internal tables while you are
debugging ABAP code.

The first method, lets you see which table required a lot of memory

1. While on debug mode follow the menu path GOTO->Display condition-


>memory use
2. Select tabstrip: Memory use - ranked list

The second method provides a very detailed list

1. While in debug mode, choose GOTO->System Areas->internal information


2. A list appears on the debug screen
3. By default the area editbox contains a ?
4. Now enter DSEG in that field.
5. Press Return .
6. You will get a list of all the tables active in that program along with some
other attributes.
7. The type and size of the records determine if they are internal tables and
how many records there are.

You can play around and try the other area than DSEG, some are interesting.

Back to top

Showing a progress bar in ABAP

This process is very easy but a lot of programmers don't know how to do it. You
just have to call the ABAP FM SAPGUI_PROGRESS_INDICATOR, at the appropriate
points.

Below is a simple example, give it a try !!

REPORT zdany_progress_bar.

DATA: percentage_text TYPE c LENGTH 5.


DO 100 TIMES.

WAIT UP TO '0.5' SECONDS.

percentage_text(3) = sy-index.

percentage_text+3 = '%'.

CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'

EXPORTING

percentage = sy-index

text = percentage_text.

ENDDO.

Back to top

Manipulating Timestamps

Actually, we should use time stamp in our abap programs instead of the
traditional date and time fields.

When we have to do some calculation on a time stamp is not as easy as on a date


field.

For example, to add an hour to a timestamp, many people will:

1. convert the time stamp into a standard date and field


2. add an hour
3. convert back the date and time to a timestamp

However, it is the slowest method you can use.


Instead, use class CL_ABAP_TSTMP.

It enables you to make whatever calculation you want on a time stamp.

In the following example, 1 hour is added

REPORT zdany_tstamp.

DATA : l_tstamp TYPE timestamp,

l_tstamp_out TYPE timestamp.

GET TIME STAMP FIELD l_tstamp.

TRY.

CALL METHOD cl_abap_tstmp=>add

EXPORTING

tstmp = l_tstamp

secs = 3600 <<<===--- 1 hour = 3600 seconds

RECEIVING

r_tstmp = l_tstamp_out.

CATCH cx_parameter_invalid_range .

WRITE 'invalid range'.

EXIT.

CATCH cx_parameter_invalid_type .

WRITE 'invalid type'.

EXIT.

ENDTRY.

WRITE l_tstamp time zone 'UTC '.

SKIP.

WRITE l_tstamp_out time zone 'UTC '.


Back to top

Generating your own standard F4 help

To avoid the standard F4 help to be show, insert the event PROCESS ON-VALUE-
REQUEST request in the program and add a field statement for the field that
should trigger the F4 help. In the module called from PROCESS ON-VALUE-
REQUEST request, call function module F4IF_FIELD_VALUE_REQUEST.

Example:

process before output.

.....

process after input.

.....

PROCESS ON VALUE-REQUEST. FIELD sflight-carrid MODULE f4_help_for_carrid.

MODULE f4_help_for_carrid INPUT.

* NOTE:

* Tabname/fieldname is the name of the table and field

* for which F4 should be shown.

* Dynprog/Dynpnr/Dynprofield are the names of the Progran/Dynpro/Field

* in which the f4 value should be returned.

* Value: The value of the Dynpro field when calling the F4 help.

* You can limit the values shown, by inseting a value in this parameter

* e.g 'A*' to show only values beginning with A

CALL FUNCTION 'F4IF_FIELD_VALUE_REQUEST'


EXPORTING

tabname = 'SFLIGHT'

fieldname = 'CARRID'

* SEARCHHELP = ' '

* SHLPPARAM = ' '

dynpprog = 'ZDANY_F4_OWN_CALL'

dynpnr = '0100'

dynprofield = 'SFLIGHT-CARRID'

* STEPL = 0

value = 'A*'

* MULTIPLE_CHOICE = ' '

* DISPLAY = ' '

* SUPPRESS_RECORDLIST = ' '

* CALLBACK_PROGRAM = ' '

* CALLBACK_FORM = ' '

* TABLES

* RETURN_TAB =

* EXCEPTIONS

* FIELD_NOT_FOUND = 1

* NO_HELP_FOR_FIELD = 2

* INCONSISTENT_HELP = 3

* NO_VALUES_FOUND = 4

* OTHERS = 5

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.


ENDIF.

ENDMODULE. " F4_help_for_carrid INPUT

To control F4 help in a selection screen use the AT SELECTION-SCREEN ON


VALUE-REQUEST FOR

<field> event.

Note that for ranges both the low and high value of the field must have there own
ON VALUE-REQUEST

Example:

AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_prctr-low.

PERFORM f4_help_carrid.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_prctr-high.

PERFORM f4_help_carrid.

Back to top

Creating an ALV Grid in 3 lines

Did you know that you can create an ALV Grid very fast; you don't need to define
a layout, a fieldcatalog, a container and all the other small things we usually
define in an ALV Grid. If we don't need to finetune the ALV Grid and just want to
display a list on the screen or to the printer, here is a very simple way to
proceed:

DATA: l_alv TYPE REF TO cl_gui_alv_grid,

lt_sflight TYPE TABLE OF sflight.

SELECT * FROM sflight INTO TABLE lt_sflight.

* Creation of the ALV object, when we use cl_gui_container=>screen0 as parent, the


ALVGrid control will
* automatically use the full screen to display the grid, NO CONTAINER DEFINITION IS
REQUIRED !

CREATE OBJECT l_alv EXPORTING i_parent = cl_gui_container=>screen0.

* calling the display of the grid, the system will automatically create the
fieldcatalog based

* on the table name you pass in parameter

CALL METHOD l_alv->set_table_for_first_display

EXPORTING i_structure_name = 'SFLIGHT'

CHANGING it_outtab = lt_sflight.

* You have to create an EMPTY screen, put NOTHING in the layout and this is going
to work

CALL SCREEN 100

Instead of creating an empty screen 100, you can also define an empty selection
screen in you program and use it, no more screen painter required !

SELECTION-SCREEN BEGIN OF SCREEN 1001.

SELECTION-SCREEN END OF SCREEN 1001.

CALL SELECTION-SCREEN 1001.

Back to top

Modifying the F4 standard calendar

When you press F4 on any date field the standard calendar opens in order for you
to choose a date. When you do not want the user to choose a weekend day or a
holiday, you can do it.

Two different type of calendars are defined in SAP, the holiday calendar and the
factory calendar.

These calendars are defined in the customizing under :


SPRO -> General settings -> maintain calendar

in this screen, all the default holidays are predefined by country, you can add or
delete holiday as you want. I tried to create the Dany's day in ISP but I'm stuck
on a small access problem... stupid security !

Because a holiday can be different by country, the factory calendar is stored for
each plants, in table T001W, field FABKL.

here is a small example on how to call the calendar :

CALL FUNCTION 'F4_DATE'

EXPORTING

date_for_first_month = '20031208'

holiday_calendar_id = 'CA' "<<==-- Here is how you point on a holiday


calendar

* factory_calendar_id = T001W-FABKL "<<==-- ... or a factory one

display = ' '

IMPORTING

select_date = select_date

EXCEPTIONS

OTHERS = 4.

Here is an example of the result, a red square marks each holiday.


Back to top

Saving an internal table in Microsoft Excel format

While you are debugging your program, you can save the content of an internal
table.

1. Display the content of the internal table in the debugger.


2. Then press ctrl-F11 or right-mouse and click "Save As Excel Worksheet".
3. Enter the From-index and To-index for the lines you want to save.
4. Choose folder/filename and save.
Back to top

Generating a number range automatically

If you need a unique key in a table or a sequential number for any other purpose,
you can use a range object .

Ranges are maintained in transaction SNRO


After maintaining the first screen you have to maintain the intervals by clicking
the button "number ranges"
These informations are stored in tables :

- TNRO Definition of number range objects

- NRIV Number range intervals. Note: Field NRLEVEL Contains the last assigned
number.

In you program you read the next number by using the function
NUMBER_GET_NEXT

DATA l_number TYPE i.

CALL FUNCTION 'NUMBER_GET_NEXT'

EXPORTING
nr_range_nr = '01'

object = 'ZTEST'

quantity = '1'

IMPORTING

number = l_number

* quantity = ' '

* RETURNCODE =

EXCEPTIONS

interval_not_found = 1

number_range_not_intern = 2

object_not_found = 3

quantity_is_0 = 4

quantity_is_not_1 = 5

interval_overflow = 6

OTHERS = 7.

display l_number.

each time you call NUMBER_GET_RANGE the number is automatically increased.

You can also create the number range in your program instead of using SNRO
with the functions:

NUMBER_RANGE_INTERVAL_LIST : verify if a number range already exist

NUMBER_RANGE_ENQUEUE : lock the specifier number range

NUMBER_RANGE_INTERVAL_UPDATE : create the number range

NUMBER_RANGE_UPDATE_CLOSE : commit changes


NUMBER_RANGE_DEQUEUE : unlock

Back to top

Defining a local type dynamically


You can now define a local type dynamically.

This is only supported in 6.40. Here's how it goes:

REPORT ZDANY_DYN_LOCAL_TYPES.

****************** hardcoded "old style" local type *******************

* This is a normal hardcoded local type

types : begin of typ_hardcoded,

l_count type i,

lt_sflight type sflight.

types : end of typ_hardcoded.

* create a table based on hardcoded type

data : lt_hardcoded type table of typ_hardcoded.

****************** dynamic "new wave" local type *******************

types: typ_count type i.

field-symbols : <lt_dynamic> type any table.

data: dref type ref to data,

itab_type type ref to cl_abap_tabledescr,

struct_type type ref to cl_abap_structdescr,

elem_type type ref to cl_abap_elemdescr,

comp_tab type cl_abap_structdescr=>component_table,

comp_fld type cl_abap_structdescr=>component.

* We read information about each fields of SFLIGHT (see ABAP FAQ #2)

struct_type ?= cl_abap_typedescr=>describe_by_name( 'SFLIGHT' ).

* We also need the information about the type "typ_count", note that

* we should use class cl_abap_elemdescr instead of cl_abap_typedescr


elem_type ?= cl_abap_elemdescr=>describe_by_name( 'TYP_COUNT' ).

* we read all fleids of SFLIGHT and create a component table

comp_tab = struct_type->get_components( ).

* We add manually the counter

comp_fld-name = 'L_COUNT'.

comp_fld-type = elem_type.

insert comp_fld into comp_tab index 1.

* we create the structure

struct_type = cl_abap_structdescr=>create( comp_tab ).

* ... and the internal table

itab_type = cl_abap_tabledescr=>create( struct_type ).

* The new thing here is the "type handle" which create a pointer to a

* handle

create data dref type handle itab_type.

* we finally assign a field symbol to the pointer because we cannot

* directly access a pointer.

assign dref->* to <lt_dynamic>.

* At the end of this small program, internal table lt_hardcoded and

* lt_dynamic are the same

break-point.

Back to top

Using macros

First : DO NOT use macros in a new program, there is a lot of better way to do it.
Some people come to my desk and tell me they found a "magic" function which
the code behind is invisible and it's impossible to trace this function. In some old
programs, you can see a strange call to what it look like a function but it's not,
it's a macro.

The macros are stored in table TRMAC.

One of the most used macros is "break". To put a break-point in your code that
will break only for your user name, you probably use "break my_user_name".
This is not part of the ABAP language despite what a lot of people think; it's a
macro. If you have a look in table TRMAC, you will see :

BREAK 000 * USER specific BREAK-POINT

BREAK 001 if sy-uname = '&1'

BREAK 002 break-point

BREAK 003 endif

Here is how to create a simple macro, for the sake of understanding what old
programs do. Again, do not

create new macros unless you absolutely must.

REPORT zdany_macro.

*Macro definition

DEFINE ZMULTIPLY.

MULTIPLY &1 BY &2.

END-OF-DEFINITION.

DATA: number1 TYPE i VALUE 2,

number2 TYPE i VALUE 5.

* calling the macro, no easy way of knowing this is a macro

ZMULTIPLY number1 number2.

WRITE number1.
Back to top

Using dynamic SELECT statements

The very useful SELECT statement could be fully dynamic from release 6.10 and
up.

For more information about the dynamic select, you can read the document
"Enhanced ABAP programming with Dynamic Open SQL" from Adrian Görler and
Ulrich Koch. Here is the link

https://webphl07.phl.sap.corp/~sapidb/011000358700002805272003E , this is a
PDF file.

Here is an example of a fully dynamic select.

REPORT zdany_dynamic_select.

* We use some parameters to dynamically control the select, this is not very

* clever but this is just a test program !!

PARAMETER : p_tabnam TYPE tabname DEFAULT 'SFLIGHT',

p_selfl1 TYPE edpline DEFAULT 'CARRID',

p_selfl2 TYPE edpline DEFAULT 'CONNID',

p_selfl3 TYPE edpline DEFAULT 'FLDATE',

p_selfl4 TYPE edpline DEFAULT 'PRICE',

p_selfl5 TYPE edpline DEFAULT 'CURRENCY',

p_where1 TYPE edpline DEFAULT 'PRICE > 300',

p_where2 TYPE edpline DEFAULT 'AND CURRENCY = ''EUR'''.

FIELD-SYMBOLS : <lt_outtab> TYPE ANY TABLE,

<ls_outtab> TYPE ANY,

<l_fld> TYPE ANY.

DATA: lt_where TYPE TABLE OF edpline,

lt_sel_list TYPE TABLE OF edpline,


lt_group TYPE TABLE OF edpline,

l_having TYPE string,

l_wa_name TYPE string,

l_sel_list TYPE edpline,

dref TYPE REF TO data,

itab_type TYPE REF TO cl_abap_tabledescr,

struct_type TYPE REF TO cl_abap_structdescr,

elem_type TYPE REF TO cl_abap_elemdescr,

comp_tab TYPE cl_abap_structdescr=>component_table,

comp_fld TYPE cl_abap_structdescr=>component.

TYPES: f_count TYPE i.

* Creation of the output table including a non standard field, f_count

* see ABAP FAQ #14 for more information on this topic

struct_type ?= cl_abap_typedescr=>describe_by_name( p_tabnam ).

elem_type ?= cl_abap_elemdescr=>describe_by_name( 'F_COUNT' ).

comp_tab = struct_type->get_components( ).

* We remove the unnecessary fields

LOOP AT comp_tab INTO comp_fld.

IF comp_fld-name <> p_selfl1 AND

comp_fld-name <> p_selfl2 AND

comp_fld-name <> p_selfl3 AND

comp_fld-name <> p_selfl4 AND

comp_fld-name <> p_selfl5.

DELETE TABLE comp_tab WITH TABLE KEY name = comp_fld-name.


ENDIF.

ENDLOOP.

comp_fld-name = 'F_COUNT'.

comp_fld-type = elem_type.

APPEND comp_fld TO comp_tab.

struct_type = cl_abap_structdescr=>create( comp_tab ).

itab_type = cl_abap_tabledescr=>create( struct_type ).

l_wa_name = 'l_WA'.

CREATE DATA dref TYPE HANDLE itab_type.

ASSIGN dref->* TO <lt_outtab>.

CREATE DATA dref TYPE HANDLE struct_type.

ASSIGN dref->* TO <ls_outtab>.

* Creation of the selection fields and the "group by" clause

APPEND p_selfl1 TO lt_sel_list.

APPEND p_selfl1 TO lt_group.

APPEND p_selfl2 TO lt_sel_list.

APPEND p_selfl2 TO lt_group.

APPEND p_selfl3 TO lt_sel_list.

APPEND p_selfl3 TO lt_group.

APPEND p_selfl4 TO lt_sel_list.

APPEND p_selfl4 TO lt_group.

APPEND p_selfl5 TO lt_sel_list.

APPEND p_selfl5 TO lt_group.


APPEND 'COUNT(*) AS F_COUNT' TO lt_sel_list.

* creation of the "where" clause

APPEND p_where1 TO lt_where.

APPEND p_where2 TO lt_where.

* Creation of the "having" clause

l_having = 'count(*) >= 1'.

* THE dynamic select

SELECT (lt_sel_list)

FROM (p_tabnam)

INTO CORRESPONDING FIELDS OF TABLE <lt_outtab>

WHERE (lt_where)

GROUP BY (lt_group)

HAVING (l_having)

ORDER BY (lt_group).

* display of the results

LOOP AT <lt_outtab> ASSIGNING <ls_outtab>.

LOOP AT comp_tab INTO comp_fld.

ASSIGN COMPONENT comp_fld-name OF STRUCTURE <ls_outtab> TO <l_fld>.

WRITE: <l_fld>.

ENDLOOP.

SKIP.

ENDLOOP.

Back to top
Using JavaScript in ABAP

You can code JavaScript directly in your ABAP code.

If you want to do some JavaScript tests in ABAP there is a good program to use :
DEMO_JAVA_SCRIPT_MINI_EDITOR. When you run this program, you can code
your Java in an ABAP editor, compile and execute it to test the result.

Nowe, here is a small example on how to insert JavaScript in ABAP:

*&---------------------------------------------------------------------*

*& Report ZDANY_JAVASCRIPT_TEST

*&---------------------------------------------------------------------*

REPORT ZDANY_JAVASCRIPT_TEST.

data: l_JS_PROCESSOR type ref to CL_JAVA_SCRIPT,

l_RETURN_VALUE type STRING,

l_SOURCE type STRING.

* Creation of a new javaScript

l_JS_PROCESSOR = CL_JAVA_SCRIPT=>CREATE( ).

* this is the Javascript code

concatenate

'var l_source = "Hello World,"; '

'l_source += " I''m"; '

'l_source += " JavaScript!"; '

'l_source; '

into l_SOURCE separated by CL_ABAP_CHAR_UTILITIES=>CR_LF.

* we compile the JavaScript code


l_JS_PROCESSOR->COMPILE( SCRIPT_NAME = 'DANYTEST.JS' SCRIPT = L_SOURCE ).

* Any syntax error ?

if l_JS_PROCESSOR->LAST_CONDITION_CODE <> 0.

write: / 'Error in COMPILE', l_JS_PROCESSOR->LAST_ERROR_MESSAGE.

exit.

else.

write / 'JavaScript was compiled'.

endif.

* Let's go and run the script

l_JS_PROCESSOR->EXECUTE( SCRIPT_NAME = 'DANYTEST.JS' ).

* Any problem during execution ?

if l_JS_PROCESSOR->LAST_CONDITION_CODE <> 0.

write: / 'Error in EXECUTE',l_JS_PROCESSOR->LAST_ERROR_MESSAGE.

exit.

else.

write / 'JavaScript was executed'.

endif.

* we read the output variable

l_RETURN_VALUE = l_JS_PROCESSOR->EVALUATE( JAVA_SCRIPT = 'l_source;' ).

write : / l_RETURN_VALUE.

Back to top

Saving an internal table in Microsoft Excel format


You can compare the source code of a program / method / function from different
systems with transaction SE39. This is very useful if you want to be sure that
your transport was successful or to see whether the same version runs on both
machines.

1. Start SE39
2. Choose Compare different systems
3. Enter both program names and the RFC destination of the remote program
4. Choose Display

Here is an example of the result, note that the system show difference in blue
You can also compare table content :

 Start transaction OY19


 Choose Manual selection
 Enter the name of the table that you want to compare
 Choose Execute
For results such as these:

Back to top

Saving an internal table in Microsoft Excel format

You can use standard program RPR_ABAP_SOURCE_SCAN to search ABAP


program code/screen logic for specific texts (strings.)
This program offers many more options than programs RSRSCAN1 or RKCTSEAR.
The benefits of using this program are as follows:

 Several texts can be entered for which the system searches. The search
text should be entered without formatting characters(*,+). Only the texts
are applied in the search, i.e. selection option conditions such as greater
than, equal to, etc., are ignored
 You can restrict the search to either the program code or the flow logic of
the selected screens or both the program code and the flow logic of the
screens.
 You can set the option to ignore the comments for search.
 Found location +/- x lines: Here we can specify how many program lines
before and after the found location of the search string should be included
in the output.
 We can also specify the option to search in all the includes used in the
specified programs.

Back to top

Managing persistent objects with object services

There is a way to avoid building a fully object-oriented program while still working
with non-object-oriented relational database. The object services layer now
provides a persistence framework that closes the object-relational gap. You no
longer need to write SQL code as objects are transparently loaded from the
database when needed. You must create a persistent class.

Choose transaction SE24 and create a persistent class; this class must be
protected.
In the class builder, a new button is added in the main toolbar - "Persistence" -
press it :

in the next screen, you just have to map which fields you need in your persistent
class

After having saved the previous screen, you will notice that the system creates a
set/get method for each field that you selected.
Activate the whole class. Now that we have a persistent object to access the
database table SFLIGHT, we must access it in a program. Here is a small example
to read/write data into SFLIGHT using persistent objects.

REPORT zdany_sflight_persistent.

DATA : l_flight TYPE REF TO zcl_dany_sflight,

l_flight_agent TYPE REF TO zca_dany_sflight,

l_seatsfree TYPE i,

l_seatsocc TYPE i.

* Reference to the class agent, ALWAYS required for any operation

l_flight_agent = zca_dany_sflight=>agent.

TRY.

*We read a record from SFLIGHT using a unique key

l_flight = l_flight_agent->get_persistent(

i_carrid = 'LH'
i_connid = '0400'

i_fldate = '20031030' ).

*We read a specific field

l_seatsfree = l_flight->get_seatsmax( ) - l_flight->get_seatsocc( ).

IF l_seatsfree > 0.

l_seatsocc = l_flight->get_seatsocc( ) + 1.

* We write back a specific field into the DB

l_flight->set_seatsocc( l_seatsocc ).

ENDIF.

ENDTRY.

COMMIT WORK.

There are lots of other methods and techniques that you can use in persistent
classes.

Contact Dany Charbonneau for more info.

Back to top

Debugging ABAP programs from JAVA applications

You can do ABAP debugging from java applications here is how :

1. Check whether your user has debugging authority. Auth. object S_DEVELOP
has to contain 'DEBUG'
o Use su01; ROLES
o Double click the different roles ->
o new session will open
o Choose Authorizations -> Display Authorization Data
2. go to the ABAP coding
3. go to the menu Utilities -> Settings / choose tab "ABAP editor " / choose
tab "Debugging" check the chek - box "Actv." in the User field enter the
user used in the Java logon (not the alias of the user!! e.g. for BRUNO enter
"3B1968D7DD1")
4. Press enter or choose OK to get back to your source code
5. If you set your next break-point a pop-up will occur to ask you if you want
to set an external break-point or a session break-point --> click external
break-point (= former HTTP break-point) -->

if you run you Java application now and the coding with the break-point is called
a new R/3 (CRM) window will open and you will see you code in debug mode
(may take some seconds)

Back to top

Defining a default variant

There are 2 different ways to define a default variant.

Method 1 - By assigning a variant directly on the transaction code. This is the


easiest way but if the program was called from another program or directly from
SE38, the default variant will be ignored.

Method 2 - By assigning a variant in the code, here is a short example of this:

* The code must be in the INITIALIZATION section

INITIALIZATION.

* We must check if a default variant was already entered by the user in a batch job
or in the transaction code

* we do not want to overwrite it ! the current variant is store in the sy-slset


field

IF sy-slset IS INITIAL.

CALL FUNCTION 'RS_SUPPORT_SELECTIONS'

EXPORTING

report = sy-cprog "actual program name

variant = 'SAPDEFAULT' "default variant name

EXCEPTIONS

variant_not_existent = 0

variant_obsolete = 0

OTHERS = 0.

ENDIF.
* After the initialization we start the real code of our program in the section
START-OF-SELECTION

START-OF-SELECTION.

Here is how to create a the variant "SAPDEFAULT" always using as a default date
today minus 30 days:

Goto -> variant -> save as variant

Here is the standard variant save screen. Enter a name and a description and
press "execute". Very few know how to handle the weird looking bottom part of
this screen:
1. Check the variables for which you want to maintain default values
2. Choose selection variables button and see the next one

The first part is a legend of the column headers (T D B Z). Because we want to
create a date, we double click on the traffic lights of the column "D" for Dynamic
date calculation. when the traffic light turn green, you can press on the down
arrow in the "T" column. This will bring a popup with all possibilities you can
imagine for defaulting a date. From a usability point of view, this is not really
good screens but nevertheless, this tool is very powerful.

Back to top

Creating Activable Breakpoints and Assertions


You can now create (de)activable breakpoints in your programs. You can release
these breakpoints in production and a customer will have the opportunity to
activate and debug your code more easily.

The following is an activable break-point : BREAK-POINT ID dany.

The checkpoint group is defined by double clicking on the checkpoint group in the
editor OR directly via transaction SAAB.

Below is the SAAB screen. In the breakpoints part of the screen, you can set it to
"inactive" or "break". All breakpoint groups will be shipped inactive by default.

Assert :

An assertion is a condition which must be true during program execution. By


using asserts in your program, you can find cause of error in shorter time. The
reaction when violating an assertion depend on a customizing and could be :

1. Start the debugger from this assert


2. Do nothing, ignore the assert
3. Log the assertion in a log
4. Terminate the program with runtime error ASSERTION_FAILED
The assertions are defined in SAAB (exactly like the breakpoints, see screen
above).Here is an example of a use for an assertion :

METHOD sort_by_name.

DATA: l_f1 TYPE i VALUE 2,

l_f2 TYPE c LENGTH 10 VALUE 'test',

l_t1 TYPE TABLE OF sflight.

the_sorting_algorithm.

ASSERT ID dany

SUBKEY 'danysub1'

FIELDS l_f1 l_f2 l_t1

CONDITION itab->is_sorted_by_name( ) <> ' '.

ENDMETHOD.

This will ensure that the table is sorted. Below is the result of the log for this
assert
Back to top

Using Shared Objects

Shared objecta are SAP's answer to an age-old request by programmers. Up until


now, the only way to share variables between programs was to use
EXPORT/IMPORT TO/FROM MEMORY. This method was very slow because the
"sending" program had to copy variables from its memory to the shared memory,
and the "receiving" program had to copy these variables from the shared memory
into its own program memory. This generated two copies for basically nothing.

Here are the benefits of shared objects:

1. Copy-free: very fast access


2. Versioning management: you can handle many version of the variables at
the same time
3. Preloading: you can now request a specific variable no matter if the
"sending" program already created them
4. Automatic propagation from a database or to different application servers
5. Could be client dependant
6. Easy monitoring
Here is a very simple example but you can do very powerful shared object, see
documentation for more information.

Step 1 :

You have to define a Root class using SE24, In this class you will defined all
variables you want to share in the attributes tab, This class must be "Shared
memory enabled" :

Step 2 : You have to define your shared memory object using the transaction
SHMA. It is important to put the class you created in step 1 in the field "root
class".
Step 3 : You have to write a program to write to the shared memory.

DATA : hdl TYPE REF TO zcl_sm_area,

root TYPE REF TO zcl_root_area.

* Create the default instance for write,

* when we attach for write, an exclusive lock is performed on the shared memory
object

hdl = zcl_sm_area=>attach_for_write( ).

*when we create our root object to access variables, we must use the new keyword
addition AREA HANDLE

CREATE OBJECT root AREA HANDLE hdl.

* Create the link between the root object and the shared memory object
hdl->set_root( root ).

* put data in the shared memory variable

root->big_var = 'hello world!'.

* commit

hdl->detach_commit( ).

Step 4 : You have to create a program to read from the shared memory

DATA : hdl TYPE REF TO zcl_sm_area.

* Open default instance for read

hdl = zcl_sm_area=>attach_for_read( ).

*Access root object component to write the variable

WRITE / hdl->root->big_var.

*Release lock

hdl->detach( ).

Step 5: You can monitor the result in transaction SHMM

Back to top

Defining dynamic orders in an ABAP sort

You can easily define dynamic fields in an ABAP sort using ( ) :

data : l_fieldname type dd03l-fieldname value 'CARRID'.

SORT sflight by (l_fieldname) DESCENDING.

BUT you CANNOT define the order (ascending or descending) dynamically :

SORT sflight by (l_fieldname) (l_order). <<==-- This is impossible in ABAP.

Now, with the new tree control, when you want to give fully customizable tree, ou
need to dynamically set the order in the sort command.

The first method is by using a CREATE SUBROUTINE POOL with your sort. Each
time you will run the program, this subroutine will be recompiled. This is VERY
slow and we should avoid using this command whenever it's possible.
The second method is a simple trick that have no major impact on performance.
You duplicate each fields you want to sort, 1 copy for ascending and 1 for
descending. You move the fieldname only in 1 of those fields depending on what
order you want.

Here is a small example on how to do that:

*&---------------------------------------------------------------------*

*& Report ZDANY_DYN_SORT

*&---------------------------------------------------------------------*

REPORT z_dany_dyn_sort.

* s_field* - field name for sorting ( F1,F2,F3,F4,F5 or space )

* s_ord* - ASC or DES ( ascending or descending)

PARAMETERS: s_field1(6) DEFAULT 'FIELD1', s_ord1(3) DEFAULT 'ASC',

s_field2(6) DEFAULT 'FIELD2', s_ord2(3) DEFAULT 'DES',

s_field3(6) DEFAULT 'FIELD3', s_ord3(3) DEFAULT 'ASC',

s_field4(6) DEFAULT 'FIELD4', s_ord4(3) DEFAULT 'DES',

s_field5(6) DEFAULT 'FIELD5', s_ord5(3) DEFAULT 'ASC'.

TYPES: BEGIN OF ltt_fields,

field1(6),

field2(6),

field3(6),

field4(6),

field5(6),

END OF ltt_fields.

DATA: l_field_asc1(6),

l_field_asc2(6),
l_field_asc3(6),

l_field_asc4(6),

l_field_asc5(6),

l_field_des1(6),

l_field_des2(6),

l_field_des3(6),

l_field_des4(6),

l_field_des5(6),

lt_fields TYPE TABLE OF ltt_fields,

ls_fields TYPE ltt_fields,

l_flag_invalid_field,

l_flag_not_asc_des.

FIELD-SYMBOLS <fs> TYPE ANY.

INITIALIZATION.

* Just to fill an internal tables for testing

DO 3 TIMES.

ls_fields-field1 = sy-index.

DO 3 TIMES.

ls_fields-field2 = sy-index.

DO 3 TIMES.

ls_fields-field3 = sy-index.

DO 3 TIMES.

ls_fields-field4 = sy-index.

DO 3 TIMES.
ls_fields-field5 = sy-index.

APPEND ls_fields TO lt_fields.

ENDDO.

ENDDO.

ENDDO.

ENDDO.

ENDDO.

START-OF-SELECTION.

* The order must be "ASC" or "DES" or space, any other value is rejected

l_flag_not_asc_des = 'X'.

CHECK ( s_ord1 = 'ASC' OR s_ord1 = 'DES' OR s_ord1 IS INITIAL ) AND

( s_ord2 = 'ASC' OR s_ord2 = 'DES' OR s_ord2 IS INITIAL ) AND

( s_ord3 = 'ASC' OR s_ord3 = 'DES' OR s_ord3 IS INITIAL ) AND

( s_ord4 = 'ASC' OR s_ord4 = 'DES' OR s_ord4 IS INITIAL ) AND

( s_ord5 = 'ASC' OR s_ord5 = 'DES' OR s_ord5 IS INITIAL ).

CLEAR l_flag_not_asc_des.

* the field name must be = "FIELD1, 2, 3, 4 or 5", any other value is rejected

l_flag_invalid_field = 'X'.

CHECK 'FIELD1FIELD2FIELD3FIELD4FIELD5' CS s_field1 AND

'FIELD1FIELD2FIELD3FIELD4FIELD5' CS s_field2 AND

'FIELD1FIELD2FIELD3FIELD4FIELD5' CS s_field3 AND

'FIELD1FIELD2FIELD3FIELD4FIELD5' CS s_field4 AND

'FIELD1FIELD2FIELD3FIELD4FIELD5' CS s_field5.

CLEAR l_flag_invalid_field.
* for a certain field, if the user ask descending order, the name of this field is

* moved in l_field_des1 AND it's important that l_field_asc1 remain empty.

IF s_field1 IS NOT INITIAL.

IF s_ord1 = 'ASC'.

l_field_asc1 = s_field1.

ELSE.

l_field_des1 = s_field1.

ENDIF.

ENDIF.

IF s_field2 IS NOT INITIAL.

IF s_ord2 = 'ASC'.

l_field_asc2 = s_field2.

ELSE.

l_field_des2 = s_field2.

ENDIF.

ENDIF.

IF s_field3 IS NOT INITIAL.

IF s_ord3 = 'ASC'.

l_field_asc3 = s_field3.

ELSE.

l_field_des3 = s_field3.

ENDIF.

ENDIF.
IF s_field4 IS NOT INITIAL.

IF s_ord4 = 'ASC'.

l_field_asc4 = s_field4.

ELSE.

l_field_des4 = s_field4.

ENDIF.

ENDIF.

IF s_field5 IS NOT INITIAL.

IF s_ord5 = 'ASC'.

l_field_asc5 = s_field5.

ELSE.

l_field_des5 = s_field5.

ENDIF.

ENDIF.

* EACH field is used twice in the sort with different name for ascending and
descending. 1 of the

* 2 fields will be empty and the sort will ignore it.

SORT lt_fields BY (l_field_asc1) ASCENDING (l_field_des1) DESCENDING

(l_field_asc2) ASCENDING (l_field_des2) DESCENDING

(l_field_asc3) ASCENDING (l_field_des3) DESCENDING

(l_field_asc4) ASCENDING (l_field_des4) DESCENDING

(l_field_asc5) ASCENDING (l_field_des5) DESCENDING.

* Display the results


EDITOR-CALL FOR lt_fields.

END-OF-SELECTION.

* if parameters was not entered correctly

IF l_flag_not_asc_des = 'X'.

WRITE: / 'Only ASC for ascending or DES for DESCENDING are allowed for fields
S_ORDn'.

ELSEIF l_flag_invalid_field = 'X'.

WRITE: / 'S_FIELDn must be = FIELD1, FIELD2, FIELD3, FIELD4 or FIELD5.'.

ENDIF.

Back to top

Inserting charts

It's a known fact that customers are very sensitive to the appeal of charts.
Creating graphical charts (bar, pie char, lines graphs) in ABAP is simple. There
are two main methods for creating charts in ABAP

 Using the class CL_GFW


 Using the function module GFW_PRES_SHOW

There are also other classes and function modules derived from these ones. Use
transaction GRAL to explore all possibilities provided by this class and this FM.

The following are two short examples of the huge potential of function module
GFW_PRES_SHOW :

* Contain the constants for the graph type

TYPE-POOLS: GFW.

DATA: VALUES TYPE TABLE OF GPRVAL WITH HEADER LINE,

COLUMN_TEXTS TYPE TABLE OF GPRTXT WITH HEADER LINE.


REFRESH VALUES.

REFRESH COLUMN_TEXTS.

VALUES-ROWTXT = 'Salary'.

VALUES-VAL1 = 50000.

VALUES-VAL2 = 51000.

APPEND VALUES.

VALUES-ROWTXT = 'Life cost'.

VALUES-VAL1 = 49000.

VALUES-VAL2 = 51200.

APPEND VALUES.

COLUMN_TEXTS-COLTXT = '2003'.

APPEND COLUMN_TEXTS.

COLUMN_TEXTS-COLTXT = '2004'.

APPEND COLUMN_TEXTS.

* Call a chart into a standard container, this function could be used for many

* different graphic types depending on the presentation_type field :

* gfw_prestype_lines

* gfw_prestype_area

* gfw_prestype_horizontal_bars

* gfw_prestype_pie_chart

* gfw_prestype_vertical_bars

* gfw_prestype_time_axis
CALL FUNCTION 'GFW_PRES_SHOW'

EXPORTING

CONTAINER = 'CONTAINER' "A screen with an empty container must


be defined

PRESENTATION_TYPE = GFW_PRESTYPE_LINES

TABLES

VALUES = VALUES

COLUMN_TEXTS = COLUMN_TEXTS

EXCEPTIONS

ERROR_OCCURRED = 1

OTHERS = 2.

Resulting in this:

The following uses the EXACT same function with a different presentation type

REFRESH VALUES.

REFRESH COLUMN_TEXTS.

VALUES-ROWTXT = ''.

VALUES-VAL1 = 10.
VALUES-VAL2 = 35.

VALUES-VAL3 = 45.

VALUES-VAL4 = 8.sul

VALUES-VAL5 = 2.

APPEND VALUES.

COLUMN_TEXTS-COLTXT = 'Fun'.

APPEND COLUMN_TEXTS.

COLUMN_TEXTS-COLTXT = 'Cars'.

APPEND COLUMN_TEXTS.

COLUMN_TEXTS-COLTXT = 'House'.

APPEND COLUMN_TEXTS.

COLUMN_TEXTS-COLTXT = 'Services'.

APPEND COLUMN_TEXTS.

COLUMN_TEXTS-COLTXT = 'Others'.

APPEND COLUMN_TEXTS.

CALL FUNCTION 'GFW_PRES_SHOW'

EXPORTING

CONTAINER = 'CONTAINER'

PRESENTATION_TYPE = GFW_PRESTYPE_PIE_CHART

X_AXIS_TITLE = 'Expenses'

Y_AXIS_TITLE = 'Expenses2'

TABLES

VALUES = VALUES
COLUMN_TEXTS = COLUMN_TEXTS

EXCEPTIONS

ERROR_OCCURRED = 1

OTHERS = 2

Back to top

Knowing when to use SELECT SINGLE or SELECT ... UP TO 1 ROWS

A lot of people use the SELECT SINGLE statement to check for the existence of a
value in a database. Other people prefer to use the 'UP TO 1 ROWS' variant of the
SELECT statement.

So what's the difference between using 'SELECT SINGLE' statement as against a


'SELECT .... UP TO 1 ROWS' statement ?

If you're considering the statements

SELECT SINGLE field INTO w_field FROM table.

and

SELECT field INTO w_field FROM table UP TO 1 ROWS. ENDSELECT.

then looking at the result, not much apart from the extra ENDSELECT statement.
Look at the run time and memory usage and they may be worlds apart.

Why is this ?? The answer is simple.


The 'SELECT SINGLE' statement selects the first row in the database that it finds
that fulfils the 'WHERE' clause If this results in multiple records then only the first
one will be returned and therefore may not be unique.

The 'SELECT .... UP TO 1 ROWS' statement is subtly different. The database


selects all of the relevant records that are defined by the WHERE clause, applies
any aggregate, ordering or grouping functions to them and then returns the first
record of the result set.

Get the difference ??

If not, here is a good example, credit for this example goes to Richard Harper, a
friend of mine on sapfans.com :

Create a Ztable called ZDifference with 2 fields in it, MANDT of type MANDT and
POSNR of type POSNR. Make sure both of these are keys. Also create a table
maintenance dialog for it (SE11->Utilities->Table Maintenance Generator). Fill
the table with ten rows 000001-000010.

Then run the program shown below:

Code:

******************************************************************

* Program: Z_Difference

* Purpose: A program that demonstrates the difference

* between SELECT SINGLE and SELECT UP TO n ROWS.

* This program requires the data table Z_DIFFERENCE

* to have been created according to the structure

* outlined in the text above and populated with

* at least 10 records.

* Creation Date: 21/04/2004

*
* Requested By:

* Reference Doc:

* Author: R Harper

* Modification History:

* Date Reason Transport Who

******************************************************************

Report Z_Difference

Message-id 38

Line-Size 80

Line-Count 0

No Standard Page Heading.

Start-Of-Selection.

Data: w_Single type Posnr,

t_Rows type standard table of Posnr

initial size 0

with header line.

Select single Posnr

from zDifference

into w_Single.
*

Select Posnr

into table t_Rows

from zDifference

up to 1 rows

order by Posnr descending.

Write :/ 'Select single:', w_Single.

Skip 1.

Write :/ 'Up to 1 rows :'.

Loop at t_Rows.

Write t_Rows.

EndLoop.

You should see the output:

Select single: 000001

Up to 1 rows : 000010

The first 'SELECT' statement selected the first record in the database according to
any selection criterion in the 'WHERE' clause. This is what a 'SELECT SINGLE'
does. The second 'SELECT' has asked the database to reverse the order of the
records before returning the first row of the result.

In order to be able to do this the database has read the entire table, sort it and
then return the first record. If there was no ORDER BY clause then the results
would have been identical (ie both '000001') but the second select if given a big
enough table to look at would be far slower.

Note that this causes a problem in the Extended Program Check if the full key is
not specified in a 'SELECT SINGLE'. Replacing the 'SELECT SINGLE' by an "UP TO
1 ROWS" will give the same exact results without any warning but the program
will run slower and consume more memory. This is a good example of a warning
that we should ignore... considering you are sure of what you are doing !!
Back to top

Working around the limitations of a range in SELECT...WHERE...IN

When you use a range table in a select (SELECT * FROM sflight WHERE carrid IN
lt_carrid), you sometimes get a short dump with the error
DBIF_RSQL_INVALID_RSQL.

A lot of people think that a maximum number of records was reached; rumour
has it that it's somewhere between 1000 and 2000 records. This is false and
groundless. In fact, the problem is that the "IN" keyword is not native SQL and
the compiler converts it into native SQL. There is a limitation on the length of the
generated SQL string.

Here is a small example of a program to create a short dump :

DATA : lt_range TYPE RANGE OF sflight-carrid WITH HEADER LINE,

lt_sflight TYPE TABLE OF sflight.

DO 5000 TIMES.

lt_range-sign = 'I'.

lt_range-option = 'EQ'.

lt_range-low = 'AA'.

APPEND lt_range.

ENDDO.

SELECT * FROM sflight INTO TABLE lt_sflight WHERE carrid IN lt_range.

The compiler convert the SELECT in native SQL like that :

... WHERE carrid = lt_range[1]-low OR

lt_range[2]-low OR

lt_range[3]-low OR

....

lt_range[5000]-low.
The short dump occur when the generated SQL caracter string is over a certain
threshold. This threshold is variable, could be between 2k and 32k (usually 4k or
8k) depending on the DB system. The threshold is stored in dbs/io_buf_size and
could NOT be change by programmers because this is maintained on DB level
(oracle, DB2, etc...) by IT.

There are two ways to avoid this problem :

1. Create some small packages (500 records) and call the SELECT within a
loop with APPENDING TABLE keyword
2. Usually the best way to do it is using a SELECT... FOR ALL ENTRIES IN...
instead.

Back to top

Calling function modules dynamically

When you only know the function name to call at run-time, you have to call your
function dynamically. Here is an example of how to call a function dynamically
(note: comments were added to an existing example from the standard help)

REPORT ZDANY_DYN_FM_CALL.

*The constants and structures required to use the dynamic function call

*is stored in the type pool ABAP.

type-pools abap.

*This is the name of the function you want to call.

data NAME type STRING value `READ_SPFLI_INTO_TABLE`.

* Parameter table, where you store all parameters, importing, exporting

* and changing data PARA_TAB type ABAP_FUNC_PARMBIND_TAB. data


PARA_LINE like line of PARA_TAB.

* Exception table to handle the exception that can occur during the

* execution of the function

data EXCP_TAB type ABAP_FUNC_EXCPBIND_TAB.

data EXCP_LINE like line of EXCP_TAB.

data CARRIER type SPFLI-CARRID.


data JTAB type SPFLI_TAB.

CARRIER = 'XYZ'.

* Name of the first parameter

PARA_LINE-NAME = 'ID'.

* type of the first parameter, could be :

* abap_func_exporting value 10,

* abap_func_importing value 20,

* abap_func_tables value 30,

* abap_func_changing value 40.

PARA_LINE-KIND = ABAP_FUNC_EXPORTING.

*We need the datatype of the parameter to pass

get reference of CARRIER into PARA_LINE-VALUE.

append PARA_LINE to PARA_TAB.

*Same thing for parameter 2

PARA_LINE-NAME = 'ITAB'.

PARA_LINE-KIND = ABAP_FUNC_IMPORTING.

get reference of JTAB into PARA_LINE-VALUE.

append PARA_LINE to PARA_TAB.

*Now we create the possible exceptions

EXCP_LINE-NAME = 'NOT_FOUND'.

EXCP_LINE-VALUE = 1.

insert EXCP_LINE into table EXCP_TAB.

EXCP_LINE-NAME = 'OTHERS'.

EXCP_LINE-VALUE = 4.
insert EXCP_LINE into table EXCP_TAB.

*... and we dynamically call the function with the parameter-table and

* exception-table addition

call function NAME

parameter-table

PARA_TAB

exception-table

EXCP_TAB.

* We check the result code

case SY-SUBRC.

when 1.

message id SY-MSGID type SY-MSGTY number SY-MSGNO.

when 2.

message E888(SABAPDOCU) with 'Error in function module'.

endcase.

Back to top

Downloading ABAP code to your local PC

Creating Z* test programs is a popular method for conducting tests. However,


when an environment is about to get shut down, keeping your test programs
becomes tricky. You have to launch se38/37/24/80, select your programs one by
one, and choose system -> list -> save -> local file, or use function module
WS_DOWNLOAD. This is a lengthy procedure that does not even enable you to
save screens, tables, or structures.

An ABAP program available for free download at http://www.dalestech.com/ can


help you save your test programs.

1. Choose home -> R/3 entreprise -> direct download entreprise 1.2
2. download the .ZIP file,
3. Unzip
4. Cut and paste in an ABAP program
5. Compile and run

All your projects (including multiple classes, programs, function groups, screens
and tables) can be saved in less than one minute.

If you want to backup your work in order to keep a certain stable version while
you are programming, it might be preferable use a tool that is already embedded
in the Workbench: in se38/37/24/80, choose utilities -> versions -> generate
version

You might also like