ABAP log

November 3, 2008

SAP NetWeaver in the comfort of your home

Filed under: ABAP, SAP — abaplog @ 6:41 pm

I always wanted to try running SAP at home, but my PC was too old to try. I could probably run the very first SAP for Linux (4.6), but I did’t even have disk space to install appropriate Linux version. It needed some very old SuSe version, while I had Ubuntu as my primary OS. Now that’s changed. After getting one of the cheap Toshiba Satellite laptops, I am able to run the latest SAP Basis on this $600 machine.

First I planned to try installing real OpenSuSe and even downloaded almost everything. Strangely, SAP requires a special JDK and the link to it can be found only through SAP OSS system – obviously not 100% free for enthusiasts to try. But there’s much better way.

One of the first things to install on my new laptop was VMware Player, to be able to run Linux inside of a virtual machine, to surf Web sites without risking my primary OS. It’s amazing technology, and it was even more amazing to find that SAP, together with Novell, is offering the latest NetWeaver trial version to be run inside a VM!

It took some time to download everything over my 6 Mbit DSL line, and a couple of hours more to extract the RAR archives. When ready, I started the VM and in less than an hour I had everything running. In short, the steps are: download, unpack, start the OS, start the SAP, request the license and install it – ready. The details are described at SDN:

https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/9110

https://www.sdn.sap.com/irj/sdn/go/portal/prtroot/docs/library/uuid/50f623ab-c08b-2a10-5e9d-d5f390a7def7

It definitely helps if you have some experience to Linux or UNIX, but it’s really easy for everyone. My own impressions so far:

I have dual core 1.86 Mhz Intel with 3 GB RAM. I guess that CPU speed is important, but RAM and disk speed are the key. Linux and SAP itself need about 1.5 GB. The VM comes with 1.5 GB preconfigured. This way you can run SAP but with SAPGUI added (it needs about 400 MB RAM), your VM doesn’t run well. I tried to increase VM memory to 2 GB. With no noticeable improvement in performance, my host OS – Vista – was half dead. I believe that having 4 or 6 GM memory will be nice, but disk speed limitations will remain. Guest OS disks, unlike CPU and RAM, are emulated as files in your Windows system and that seems to be a bottleneck. Even if you increase VM memory to 2.5 GB (I think it’s maximum allowed by VM Player), it won’t be enough to cache all SAP data in memory minimizing disk access. Perhaps running with VM Server, which is also free now, can help, but you will never get the best of your hardware, as you would if you ran “real” Linux.

Note: 32-bit Vista supports only 4 GB, and even with 4 GB Windows doesn’t always use all 4. See Microsoft KB 929605.

To improve the performance I tried to put the number of SAP processes down, but it was not successful – I am not a Basis specialist. It has to be pretty easy but those RZ04 and RZ10 transactions didn’t work for me. The good thing, however, is that if anything happens, you can always restore your virtual system by copying or unpacking the original files.

What I decided to do was running SAPGUI on my host OS. You can only get Java version for free (not as OSS download) and it’s really memory hungry. Running it on VM is a waste of resources if I can do that “directly” in Windows. The only problem was to configure the connection to virtual machine. The above mentioned Web log gives some hints at how to do that, but the key for me was peeking at SAPGUI configuration in the VM. Normally you won’t have SAP Logon configuration files that will have SAP gateway information, but luckily, I tried to look at the extended configuration tab in SAP Logon, and the mysterious connection string that I found there worked for me.

June 16, 2008

Recoding document numbers

Filed under: ABAP, APO, SAP — abaplog @ 7:57 pm

For one of our external systems we collect the data from both R/3 and APO and last week I had an interesting problem of potential conflict between document numbers. The planned orders that are created in APO but not published to R/3 are (hopefully) guaranteed to have different document numbers than those existing in R/3. I guess you can configure the systems so that they will be using the same number range. So, if you are transferring R/3 and APO planned orders, there should not be any problems in mixing documents.

Now, imagine we have to transfer the material reservations of the planned order. If not all BOM components are known to APO, R/3 does the BOM explosion when the APO order gets published into R/3. If the order is not published, I have to make the BOM explosion in R/3 myself. How do I assign reservation numbers in a safe way? My first idea was to simply use the planned order number as reservation number abd the BOM position and the reservation position number. Wrong way. This does not ensure that the reservation numbers you “simulate” this way differ from real ones from the RESB table, because planned orders and reservations use different number ranges.

What saved me was that the reservation number for my external system doesn’t have to be purely numeric. Actually, even in R/3 itself it’s defined as “character” of length 10, and it’s only number range logic that results in numeric reservation numbers. How can we encode the numbers easily so that they will be distinct? I started looking at hexadecimal encoding. I needed to squeeze ten decimal positions into less, to save at least one position to use it as a prefix, something like “A” at the beginning, to make the resulting number (ok, not the number but “identifier”) 100% distinct. For if left just hexadecimal, we can easily have a hexadecimal number consisting just of digits, and we may end up with conflicting document numbers again.

If we split the maximum document number into two parts, i.e. two of 99999, each part can be represented with a hex number of 1869F. Same five positions! But the first one needs only one bit, it’s either zero or one! Thus, taking last four positions of both parts, and combining first two positions together into one hex digit, I can encode the whole thing into exactly nine positions, leaving one for my prefix. For example, 9999999999 will be coded like 03869F869F, and the external system will get something like A3869F869F.

The rest is the ABAP code that does the encoding:

form encode_via_hex
  using pf_char10
  changing pf_char10_out.
 
  data:
    lf_num51(5) type n,
    lf_hex4(4)  type x,
    lf_num1(1)   type n,
    lf_char8(8 ) type c.
 
  pf_char10_out = '0000000000'.
 
* first part
  lf_num51 = pf_char10(5).
  lf_hex4 = lf_num51.
 
  write lf_hex4 to lf_char8.
  lf_num1 = lf_char8+3(1) * 2.
  pf_char10_out+6(4) = lf_char8+4(4).
 
* second part
  lf_num51 = pf_char10+5(5).
  lf_hex4 = lf_num51.
 
  write lf_hex4 to lf_char8.
  lf_num1 = lf_num1 + lf_char8+3(1).
  pf_char10_out+2(4) = lf_char8+4(4).
  pf_char10_out+1(1) = lf_num1.
 
endform.

May 2, 2008

SAP APO Simsessions cleanup

Filed under: ABAP, APO, SAP — abaplog @ 6:56 pm

As I wrote last week, we had some problems with data not being released when making a call from R/3 to APO via RFC. I expected the data – the sumulation sessions of APO liveCache – to be cleaned up as soon as the RFC call is getting back into R/3, but they remained in memory as long as our dialog transaction on R/3 side was kept open. This was a kind of concern for our team as a problem potentially affecting liveCache performance, though in liveCache monitoring (LC10), the memory consumed by those simsessions looked quite innocent in comparison with others.

So, what was that? Whenever we want to read some liveCache data, we need to open a “session” first, for it is a required parameter to most of APO functions reading data from or writing to liveCache. Exceptions are of course BAPIs, that are dedicated to be called easily from outside. And if you are in some user exit of BADi, you don’t need the session because normally you are given one as a parameter.

So, before we start, we usually create a new session with the function /SAPAPO/RRP_SIMSESSION_CREATE. If it’s being done in a “short-living” fashion, like a report that is run and then exited, one’s sessions are gone very soon. But ours was kept open by some users, sometimes for hours. LiveCache deletes the sessions that it finds to be too old (as defined in customizing), but we wanted them to go away faster. In the same function group as CREATE function, there is the function /SAPAPO/RRP_SIMSESSION_LEAVE, but I found it to be slightly suspicious, for it is used by SAP itself only at couple of places. In addition, the code I had to fix could be calling CREATE function several times. Do I have to call LEAVE every time right after? Or are there any other ways?

I decided to search on OSS. There are many notes dealing with sessions that remain open, and I looked at how SAP solved the problem. They did it better than just calling LEAVE. Instead, before calling CREATE, they try to find any existing session created earlier in the program by calling /SAPAPO/RRP_SIMSESSION_GET. If it fails, they create a new one and set a flag (a global variable) indicating that they have to release the created session. I decided to use the same logic, so the whole sequence looked like:

*   Try to get an existing session first
    call function '/SAPAPO/RRP_SIMSESSION_GET'
      importing
        ev_simsession = lv_simsession.
    if lv_simsession is initial.
*     Create a new simsession
      call function '/SAPAPO/RRP_SIMSESSION_CREATE'
        exporting
          iv_simid       = ls_params-simversion
          iv_change_mode = gc_false
        importing
          ev_simsession  = lv_simsession.
      gf_sims_created = gc_true.
    endif.

* ..... now depending on program conditions,
*       follow more GET/CREATE blocks, with GET
*       most likely returning the session created earlier

* And now, cleanup:

* Did we create any sessions?
  if gf_sims_created = gc_true.
*   Check if there are any sessions still open
*   (who knows - maybe they are deleted already)
    call function '/SAPAPO/RRP_SIMSESSION_GET'
      importing
        ev_simsession = lf_simsession.
    if not lf_simsession is initial.
      call function '/SAPAPO/TSIM_SIMULATION_CANCEL'
        exceptions
          others = 0.
      gf_sims_created = gc_false.
    endif.
  endif.

Update: as our practice has shown, this way of releasing APO SIM sessions is good only if your program does exactly one RFC call to APO. If you do two, the second one will fail because APO will try to reuse the old session which is already destroyed – the result of APO keeping the data and program state from the first call and therefore some global variable not being initialized. So, in this case the right way is calling the /SAPAPO/RRP_SIMSESSION_LEAVE.

February 4, 2008

Scary SAP transactions.

Filed under: SAP — abaplog @ 7:02 pm

Sometimes SAP can be really scary. Just look what do they have in ERP 6.0 release:

acc.jpg

A must transaction for every business traveller!

Or look at this list of possible employee dismissal reasons for some HR transaction in 4.6:

dismiss.jpg

Would they please be so fair to declare unjustified dismissal?

The Data Dictionary also has some strange values list for the domain BEN_LNCOD that belongs to HR.

Value: Accidental Death or Dismemberment

ok, I understand this one, after seeing the machines used at our plants.

Value: Death and Dismemberment

When compared with the first value, do they mean now it’s a voluntary dismemberment or what, if it’s not accidental? Like a lizard throwing off its tail probably.

And finally:

Value: Supplemental Death and Dismemberment

Supplemental, you say?

December 18, 2007

Dusty ABAP snippets.

Filed under: ABAP, SAP — abaplog @ 8:29 pm

Many code samples or tips I post here originate in my collection that was built in last years. When I discover or code something worth keeping, I strip company specifics from the code and save it in simple text files. Some code snippets are used again and again, like macros in this post below or data caching forms. But once in a while I have to reuse the code after blowing away the dust.

That was back in 2000 when I have to do some report that, among other things, had to show costing estimates from the product costing runs. I don’t remember any more how did I find the solution (probably after my functional analyst gave up trying to help me to get the data easily), but it was using a couple of BAPI functions. And two weeks ago, after getting a similar requirement and having spent some time with debugger and asking FI/CO colleagues and everyone else, I have thought that this is actually a “deja vu”. Quick check of my code library – YES! I had to do some adjustments to the code and use the debugger to get the right constants corresponding to my company’s logic, but was still a wonder that my old code did. Here it goes:

* Macro to prefill select option

DEFINE ADD_SELECTION_VALUE.

  &2-SIGN = 'I'.

  &2-OPTION = 'EQ'.

  &2-LOW = &1.

  APPEND &2.

  CLEAR &2.

END-OF-DEFINITION.


form get_cost_element
  using
    pi_plant type werks_d
    pi_matnr type matnr
    pi_date  type sydatum
  changing
    po_cost  type total_amt.
 
  data:
    lt_cvar  type standard table of bapicstgva with header line,
    lt_matnr type standard table of bapimateri with header line,
    lt_plant type standard table of bapiplant  with header line,
    lt_costs type standard table of bapisplitt with header line.
 
  data: begin of it_list occurs 0.
          include structure bapicolist.
  data: end of it_list.
 
  data:
    ls_cost_header like bapiheader.
 
  data:
    ls_ret   type bapireturn,
    lf_found type char1 value ' '.
 
* ABCD is the costing variant. It should be your company specifics.
  add_selection_value 'ABDC' lt_cvar.
  add_selection_value pi_matnr lt_matnr.
  add_selection_value pi_plant lt_plant.
 
  clear: po_cost.
 
  call function 'BAPI_COSTESTIMATE_GETLIST'
       importing
            return             = ls_ret
       tables
            costing_variant    = lt_cvar
            material           = lt_matnr
            plant              = lt_plant
            cost_estimate_list = it_list
       exceptions
            others             = 1.
  check sy-subrc = 0.
* sort to get result of the last costing run
  sort it_list by valid_from descending.
* XX and YYY should be specific to your task.
* Check with debugger after calling the first BAPI
  loop at it_list where cstg_type = 'XX' and
                        vltn_vrnt = 'YYY'.
    if lf_found = 'X'.
      exit.
    endif.
*   Get first costing suitable by date
    if pi_date >= it_list-valid_from and
       pi_date  0.
          po_cost = lt_costs-total_amt.
          lf_found = 'X'.
          exit.
        endloop.
      endif.
    endif.
  endloop.
 
endform.

December 9, 2007

New frontiers of APO

Filed under: ABAP, APO, SAP — abaplog @ 3:26 pm

The capture of new markets by SAP flagship solutions like APO is sometimes missed by the press. But you can still discover the advances in the least expected ways.

This happened to me when the girl in our drug store offered me the best solution she had to my flu problem, earlier this year:

apo.jpg

Well, as long as the solution is “Made in Germany”, it doesn’t hurt, right? So I decided to give it a try and I still remain very satisfied with it. After all, the cost is quite reasonable and you DO get ROI after just several days of use! (No kidding here. I normally don’t trust the homoeopathic stuff but this one works wonderfully.)

So, let me give you my advice:

If your APO makes you sick, you just need a different version. Like that one on the photo above! 

November 22, 2007

Your fault! (Using SAP logging in your ABAP.)

Filed under: ABAP, SAP — abaplog @ 8:50 pm

The most common problem explanation we hear from users is that there should be a program error that triggers when users do everything right. While we never get rid of mistakes we do, we have to admit this special class of elusive errors that magically disappear as soon as we can trace every critical step in the program flow. The key to solution is logging. Sure, you cannot log everything, but only the most critical points. What I have to do sometimes is to add a temporary logging capability to investigate problem popping up in production system. So, let’s see what we can do to give us more chances to smack users with a log and say “Your fault!” 🙂

There are numerous simple ways to show and store messages from your program. What’s important is that they have to be stored for some time to allow analysis after they happen. For example, if it’s a report or a program that is run in background most of the time, you have obvious choices of printing messages into the spool with normal WRITEs or using MESSAGE to have it stored in the job log. Both things can be found then in SM37.

If you want logging capabilities for an online report or a dialog program, you have to use other techniques. It is the same also for user exits that are called from SAP standard transactions or things that run in background by definition, like interfaces (e.g. APO CIF).

The simplest trick I am using in development to check program logic that cannot be easily debugged is my own table, a local object, with a long text field to store my messages and probably couple of other fields like date/time to distinguish the records. The table content is written by simple MODIFY. I usually have a program that zaps the content of my logging table, to clean up the mess.

When you are going to use this kind of logging in a “real” system, some things have to be taken care of:

  1. Uniqueness of records / keys, especially in case when the messages can be written by different program instances running at the same time
  2. More intelligent way of log reorganization (schedule).

When in development system, I can simply use the current time as the table key because I know that “I’m alone”. The official way of generating unique keys in SAP, however, is using number ranges. Briefly, you have number ranges for, say, production orders, and number ranges for materials etc. For each number range object, SAP can generate you the “next number” when you call a special function. This functionality is managed centrally and numbers will be unique even if you call the function on different application servers. The disadvantage of number ranges is that they have to be customized and monitored for “exhaustion”.

To save us the effort of creating a new number range object, customizing its range and monitoring, we can use another technique of generating unique numbers (or strings) that can then be used as part of table key: Global Unique Identifiers (GUIDs). Normally this is a long string, generated to be “almost” unique for a given system. The chance of having duplicate GUIDs is so small that I have never seen any checks whether to ID exists already. SAP uses GUIDs everywhere in APO, providing tables that map readable names / codes and internal identifiers – GUIDs. Those are normally 22 or 32 characters long. You can have a GUID generated for you by calling the function GUID_CREATE. When you add date/time as key fields into your logging table, you get a pretty simple logging system without any customizing. An additional reorganization program can be run periodically to have the entries older than, say, one month, deleted. Alternatively, if some errors have to be analysed and fixed (like problems with missing master data etc), you can provide table maintenance dialog (SE11/SE54) so that responsible user can delete checked log records after analysing them.

And finally let’s have a look at the real logging, as blessed by SAP. SAP Application Log, transaction SLG1, is the most common way to store messages. Log entries are created and stored using a set of functions. Logs are grouped by log objects and subobjects. For example, you have log group for object PPORDER (production order) and you can further classify logs with production order subobjects HEADER and OPERATION. Those are selections that you use on the initial screen of SLG1, along with others like: free text name / ID, date/time, user name and transaction code.

When creating a log entry, you can tell the system how long should it be stored before it’s deleted automatically. Unlike in the above case with your own log table, you don’t need a reorganization program. I normally request the log messages to be stored for 30 days, it can be less if you feel like your system will not be happy to carry the weight.

Enough with discussion, let’s see the code now:

* this include contains log system constants and has to be used
* in your program (or top include if it's a function group)
  include sbal_constants.

* the form writes a single log entry to application log,
* building log entry identifier from parameters pf_par1 and pf_par2,
* and writing the content of other parameters into the message
  form write_log
    using pf_par1
          pf_par2
          pf_str1  type  char50
          pf_str2  type  char50
          pf_str3  type  char50
          pf_str4  type  char50.

    data:
       ls_log        type bal_s_log,
       lt_handle     type bal_t_logh,
       lf_handle     type balloghndl,
       ls_msg        type bal_s_msg.

* we use production order / operation log objects
    ls_log-object     = 'PPORDER'.
    ls_log-subobject  = 'OPERATION'.
    concatenate 'Some_Name_' pf_par1 '_' pf_par2
      into ls_log-extnumber.
    ls_log-aluser     = sy-uname.
    ls_log-alprog     = sy-repid.
    ls_log-altcode    = 'YOUR_TCODE'.
    ls_log-aldate_del = sy-datum + 30.  "keep for one month
    ls_log-del_before = 'X'.

*   create a log
    call function 'BAL_LOG_CREATE'
         exporting
              i_s_log      = ls_log
         importing
              e_log_handle = lf_handle
         exceptions
              others       = 1.

*   define data of message for Application Log
*   Use generic message template with & & & &
    ls_msg-msgty     = 'S'.
    ls_msg-msgid     = '01'.
    ls_msg-msgno     = '319'.
    ls_msg-msgv1     = pf_str1.
    ls_msg-msgv2     = pf_str2.
    ls_msg-msgv3     = pf_str3.
    ls_msg-msgv4     = pf_str4.
    ls_msg-probclass = probclass_none.

*   add this message to log
*   this function can be called several times to have one log entry
*   store several different messages
    call function 'BAL_LOG_MSG_ADD'
         exporting
              i_log_handle = lf_handle
              i_s_msg      = ls_msg
         exceptions
              others       = 1.

*   save the log
    append lf_handle to lt_handle.
    call function 'BAL_DB_SAVE'
         exporting
              i_save_all     = 'X'
              i_t_log_handle = lt_handle
         exceptions
              others         = 1.

  endform. 

Note that here we are using a generic message (01/319). This way you are free to write anything you want but then you don’t get “long text” of the message when clicking it in the SLG1. Alternatively, we could use application specific message class, assigning it and message variables to appropriate fields of the structure bal_s_msg.

November 12, 2007

Copying SAP ALV layouts between clients.

Filed under: ABAP, SAP — abaplog @ 8:44 pm

When my colleague wanted to check some standard SAP reports, he got the following problem: the system had two different clients (mandants), one of them had the test data and in another one the ALV layouts were saved. You can create a transport request from ALV maintenance screen when in the list output, but for that, you had to have some data (this particular report stopped with an error message at the selection screen). It was known to him that ALV layouts can be transported with a separate program, RKKBALVI, but it seems that the program name indicated in the status line was a different one. After doing the usual trick with debugger (going to layout transport screen and entering /h to get into debugger), we could figure out that the report RKKBALVI actually calls the function module LT_VARIANTS_IMPORT_SELECTION, which can also be used separately. But best of all, we could notice in the debugger that the program name used for RKKBALVI was completely wrong! With the right program name, both ways (function module and SAP-supplied program) worked perfectly.

October 10, 2007

Navigating component screen of CO02 with BDC.

Filed under: ABAP, SAP — abaplog @ 8:10 pm

Creating and changing production orders in SAP R/3 is a common task in production planing. While you can easily create an order from your ABAP program and do some simple changes using BDC technique, things get ugly when you need to do some more complex tasks. Unfortunately, there are no BAPIs or official function modules to cover every possible operation. Creating orders and changing their operation parameters can be done using “Production Optimization Interface” functions, as I described once here on ABAPlog. Yes, we all know that a couple of years ago SAP promised us to cover complete functionality of SAP with its SOA stuff (ESA in SAP lingo) till the end of 2007, but it doesn’t look like they are going to hit that. Therefore, we poor ABAP developers have to stick with BDC as the last but proven solution.

The most common requirement, apart from changing the header and operation data, is changing the component list. This could be relatively easy with BDC, but only if you are always sure that you have just a few of components that fit on the same page of the table control in the component overview of CO02. Unlucky ones have to deal with the navigation. Some general ideas are given in the BDC article of the ABAP Knowledge Corner from Richard Harper. The main point is: look for navigation commands in menu. Even if you don’t see them, you still have a chance that they are not present in the GUI status but nevertheless supported (you have to study the source code of the SAP transaction to find that out). But CO02 have a wonderful command in its component overview screen: “Find”. Clicking it brings a popup window:

co02.gif

For BCD, it’s the screen 0110 of the program SAPLCO05. By filling some parameters and clicking Enter, you land on the corresponding line. This line will be the first one in the table control and thus easily handles by your batch input. Before using the navigation screen, it is normally a good idea to navigate to the top of the list using the corresponding command.

Let’s see how can we use the navigation. I assume first that each ABAP BDC program defines a form to populate BDC data table, something like this:

form dynpro using dynbegin name value.
  if dynbegin = 'X'.
    clear t_bdc_tab.
    move: name                    to t_bdc_tab-program,
          value                   to t_bdc_tab-dynpro,
          'X'                     to t_bdc_tab-dynbegin.
    append t_bdc_tab.
  else.
    clear t_bdc_tab.
    move: name                    to t_bdc_tab-fnam,
          value                   to t_bdc_tab-fval.
    append t_bdc_tab.
  endif.
endform.

The following form will change the sort string (normally the very last column) for item identified with position number and material:

form change_sort_string
  using p_pos p_newsortval p_matnr.
  perform dynpro using:
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'P--',  "Go to top pos first
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'AUFS',  "Find item
    'X' 'SAPLCO05' '0110',
    ' ' 'RCOSU-POSNR' p_pos,
    ' ' 'RCOSU-MATNR' p_matnr,
    ' ' 'BDC_OKCODE' 'MORE',  "Enter to navigate
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_CURSOR' 'RESBD-SORTF(01)',
    ' ' 'RESBD-SORTF(01)' p_newsortval,
    ' ' 'BDC_OKCODE' '/00',  "Enter to change pos number
    'X' 'SAPLCOMD' '0110',
    ' ' 'BDC_OKCODE' '/00'.  "confirm
endform.

And this one will delete the item, also identified with its position number and material:

form delete_pos_by_number using    p_posnr p_matnr.
  perform dynpro using:
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'P--',  "Go to top pos first
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'AUFS',  "Find item
    'X' 'SAPLCO05' '0110',
    ' ' 'RCOSU-POSNR' p_posnr,
    ' ' 'RCOSU-MATNR' p_matnr,
    ' ' 'BDC_OKCODE' 'MORE',  "Enter to navigate
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_CURSOR' 'RESBD-POSNR(01)',
    ' ' 'RC27X-FLG_SEL(01)' 'X',  "select 1st item in the list
    ' ' 'BDC_OKCODE' 'DEL',  "delete selected item
    'X' 'SAPLCOMD' '0110',
    ' ' 'BDC_OKCODE' '/00'.  "confirm
endform.

October 4, 2007

Using ABAP Native SQL with SAP Namespaces.

Filed under: ABAP, SAP — abaplog @ 7:58 pm

Starting, I think, with SAP R/3 4.6, we can use namespaces to name our objects. (“We” includes, in this case, SAP itself too.) That means that instead of using the Y- or Z-prefix to distinguish between SAP’s objects (like tables or programs) and customer or third-party objects, we can register a namespace with SAP that will be used as object name prefix exclusively by our company, thus preventing name conflicts. SAP itself is using, for example, /SAPAPO/ namespace for all objects specific to APO. The feature was logical to expect from SAP to add it, but sometimes it is bringing problems.

Till now all I had were some warning messages when activating tables or ABAP programs (already forgot what was that exactly). But today, when I was writing DELETEs in Native SQL, I had to spend some time to figure out what kind of syntax does it want. The problem was that when the native SQL statement was sent to our IBM DB6 database, it was causing a short dump. I did not expect it to be happy though, and tried to enclose my table names that had slashes in them into single quotes. Doesn’t work. I used the SQL Trace (ST05) then to see which syntax does SAP use internally. They used double quotes. (Note that unless it is the native SQL, double quotes will be considered a comment opening.) But double quotes didn’t help me either – it was still crashing.

I did a quick search in OSS and found one rather unrelated note that among other things mentioned that in addition to double quotes, all table and column names should be in upper case. This was my last trouble to fix. My ABAP Pretty Printer setting is “all lowercase”, and though the pretty printer doesn’t touch the native SQL, I typed everything in lower case myself. If I only copied the syntax that was shown in the “Detail” screen of the SQL Trace, my problem would be solved right away! But well, it was a nice little trick for me to learn.

Older Posts »

Blog at WordPress.com.