Skip to Content
Author's profile photo Matthew Billingham

In case of a sensitive range, apply this solution

The problem

In the documentation of CP we see:

CP Covers Pattern: True if the content of operand1 fits the pattern in operand2. Wildcard characters can be used to create the operand2 pattern, where “*” represents any character string (including a blank string) and “+”represents any character. It is not case-sensitive. Trailing blanks in the left operand are respected. If the comparison is true, sy-fdpos contains the offset of operand2 in operand1. Here, leading wildcard characters “*” in operand2 are ignored if operand2 also contains other characters. If the comparison is false, sy-fdpos contains the length of operand1. Characters in operand2 can be selected for direct comparisons by prefixing them with the escape character “#”. For characters flagged in this way in operand2, the operator is case-sensitive. Also, wildcard characters and the escape character are not subject to special handling and trailing blanks are relevant.

You see that bit in red? That means ‘abc’ CP ‘Ab*’.

Good to know.

So, what about in select-options (and ranges)? Perhaps we’ve a bunch of RFC destinations in table RFCDES, and we’ve got destinations MATT, matt, and Matt. Slightly strange RFC destinations – one wonders about the narcissism of the definer!

So, I’ve got a select-option

DATA g_rfcdest TYPE rfcdest.
SELECT-OPTIONS so_rfc FOR g_rfcdest.

In my select option, I have the single entry Ma*. What happens with a select from the database?

SELECT rfcdest INTO TABLE @data(destinations) WHERE rfcdest IN @so_rfc.

Great – it works. I end up with a table, containing only Matt. Let’s try the same with an internal table.

SELECT rfcdest INTO TABLE @DATA(destinations) FROM rfcdes.
DELETE destinations WHERE rfcdest NOT IN so_rfc.

Oh. It doesn’t work. It turns out that with select options that have patterns, if they’re used with internal tables, the match is case-insensitive.

So what now? Is it so insensitive that it reduces us to tears?

A solution

To a man with hammer, everything is a nail. I’m an ABAPper, so naturally, I’m going for a programmatic solution.

Well, the documentation with CP says that we should use # as an escape character. If my range is 10 characters, and I only want Ma* then I supply #M#a*. OK. But now I want to see if there are matches to Matthe* – so I supply #M#a#t#t#h#e*. Oops – that 13 characters. Too long for the range.

What might not be widely appreciated is, that with a range or select option, it doesn’t matter too much what the actual type used is for the range. So even if my internal table field is ten characters, I could, for example, use a range of strings, and see if my entry is IN that.

So that’s what I did. A couple of small utility methods to do the conversion. And in 7.4 and above, you can make use of some of the new language features to make it very tidy indeed.

REPORT zcs_range.

CLASS lcl_case_tools DEFINITION.

  PUBLIC SECTION.
    TYPES ty_string_range TYPE RANGE OF string.
    METHODS get_string_range
      IMPORTING
        i_range               TYPE STANDARD TABLE
      RETURNING
        VALUE(r_string_range) TYPE ty_string_range.

    METHODS make_case_sensitive
      IMPORTING
        i_value           TYPE clike
      RETURNING
        VALUE(r_cs_value) TYPE string.

  PRIVATE SECTION.

    METHODS is_letter
      IMPORTING
        i_letter           TYPE string
      RETURNING
        VALUE(r_is_letter) TYPE abap_bool.

ENDCLASS.

CLASS lcl_case_tools IMPLEMENTATION.

  METHOD get_string_range.
    r_string_range = VALUE #( FOR wa IN CORRESPONDING ty_string_range( i_range )
                                   ( sign   = wa-sign
                                     option = wa-option
                                     low    = me->make_case_sensitive( wa-low )
                                     high   = me->make_case_sensitive( wa-high ) ) ).
  ENDMETHOD.

  METHOD make_case_sensitive.
    IF i_value IS INITIAL.
      RETURN.
    ENDIF.

    DATA(pos) = 0.
    DO.
      IF pos EQ strlen( i_value ).
        EXIT.
      ENDIF.
      DATA(single_char) = substring( val = i_value off = pos len = 1 ).
      r_cs_value = r_cs_value && COND #( WHEN me->is_letter( single_char ) THEN '#' ) && single_char..
      ADD 1 TO pos.
    ENDDO.

  ENDMETHOD.

  METHOD is_letter.
    r_is_letter = xsdbool( to_upper( i_letter ) CA sy-abcde ).
  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.


  TYPES ty_stuff TYPE STANDARD TABLE OF char3 WITH NON-UNIQUE KEY table_line.

  DATA(stuff_to_handle) = VALUE ty_stuff( ( 'abc' ) ( 'ABC' ) ( 'aBC' ) ).
  cl_demo_output=>begin_section( 'The stuff' ).
  cl_demo_output=>write_data( stuff_to_handle ).

  DATA stuff_range TYPE RANGE OF char3.
  stuff_range = VALUE #( ( sign = 'I' option = 'CP' low = 'A*' ) ).

  cl_demo_output=>next_section( 'The range' ).
  cl_demo_output=>write_data( stuff_range ).

  DATA(ncs_stuff) = stuff_to_handle.
  DELETE ncs_stuff WHERE table_line IN stuff_range.
  cl_demo_output=>next_section( 'Non-case sensitive removal' ).
  cl_demo_output=>write_data( ncs_stuff ).

  DATA(cs_stuff) = stuff_to_handle.
  DELETE cs_stuff WHERE table_line IN NEW lcl_case_tools( )->get_string_range( stuff_range ).
  cl_demo_output=>next_section( 'Case sensitive removal' ).
  cl_demo_output=>write_data( cs_stuff ).
  cl_demo_output=>end_section( ).

  DATA(pattern) = 'Ab*'.
  cl_demo_output=>begin_section( 'The pattern' ).
  cl_demo_output=>write_data( pattern ).

  cl_demo_output=>next_section( '''abc'' CP ''Ab*''' ).
  IF 'abc' CP pattern.
    cl_demo_output=>write_text( 'It covers it' ).
  ELSE.
    cl_demo_output=>write_text( 'It does not cover it' ).
  ENDIF.

  cl_demo_output=>next_section( 'Case sensitive ''abc'' CP ''Ab*''' ).
  IF 'abc' CP NEW lcl_case_tools( )->make_case_sensitive( pattern ).
    cl_demo_output=>write_text( 'It covers it' ).
  ELSE.
    cl_demo_output=>write_text( 'It does not cover it' ).
  ENDIF.

  cl_demo_output=>display( ).

 

I do have a couple of questions.

  1. Does anyone know where the case-insensitivity of select-options/ranges with respect to internal tables is documented. I couldn’t find it.
  2. How do select-options/ranges work with selects from internal tables? I’m assuming they’ll be case sensitive – but unfortunately, I don’t have such a system to play with.

 

 

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sandra Rossi
      Sandra Rossi

      I don't know if these are the documentations you're looking for:

      itab ... WHERE ... IN ... : https://help.sap.com/http.svc/rc/abapdocu_752_index_htm/7.52/en-US/index.htm?file=abenlogexp_select_option.htm

      OpenSQL ... WHERE ... IN ... : https://help.sap.com/http.svc/rc/abapdocu_752_index_htm/7.52/en-US/index.htm?file=abenwhere_logexp_seltab.htm

      • "The selection table is evaluated in the same way as in comparison expressions, with the difference that any comparisons using the operators CP and NP are transformed into LIKE conditions [...] LIKE conditions are case-sensitive, which is not the case in ABAP comparison expressions."

       

      Author's profile photo Matthew Billingham
      Matthew Billingham
      Blog Post Author

      Yeah  That's the one

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Thanks for sharing, Matt (or MATT 🙂 )! I still don't get it though why CP can't just be case-sensitive...

      Author's profile photo Matthew Billingham
      Matthew Billingham
      Blog Post Author

      Probably it's been like that too long and it's too late to change it now.