Skip to Content
Technical Articles
Author's profile photo Jan Braendgaard Petersen

Calculate hash value for internal table

Introduction

This might not be groundbreaking tech-news, but it could benefit someone diving into ABAP unit testing for the first time.

From time to time the result of the method under test would be an internal table. And how to use this in the assertion for unit testing? Well, calculate the hash value for the entire internal table, and voila – assertion is possible.

 

Method to calculate hash value

METHODS:
  get_hash_value_for_itab
    IMPORTING
      it_table TYPE ANY TABLE
    EXPORTING
      ev_hash  TYPE hash160.
METHOD get_hash_value_for_itab.
    DATA: lv_string  TYPE xstring,
          lv_xbuffer TYPE xstring.

    " Clear the hash value
    CLEAR ev_hash.

    " Go through the table one line at a time
    LOOP AT it_table ASSIGNING FIELD-SYMBOL(<ls_line>).
      DATA(lv_index) = 0.
      CLEAR lv_string.

      " Concatenate all the columns of the line into one string
      DO.
        ASSIGN COMPONENT lv_index OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_field>) CASTING TYPE x.
        IF sy-subrc EQ 0.
          CONCATENATE lv_string <lv_field> INTO lv_string IN BYTE MODE.
          ADD 1 TO lv_index.
        ELSE.
          EXIT.
        ENDIF.
      ENDDO.

      " Add the string from the line to our buffer
      CONCATENATE lv_xbuffer lv_string INTO lv_xbuffer IN BYTE MODE.
    ENDLOOP.

    " Calculate the hash value for the entire buffer/table
    CALL FUNCTION 'CALCULATE_HASH_FOR_RAW'
      EXPORTING
        data           = lv_xbuffer
      IMPORTING
        hash           = ev_hash
      EXCEPTIONS
        unknown_alg    = 1
        param_error    = 2
        internal_error = 3
        OTHERS         = 4.
    IF sy-subrc <> 0.
      " Do error handling here
    ENDIF.
  ENDMETHOD.

Usage of method

DATA:
      lv_hash_old       TYPE hash160,
      lv_hash_new       TYPE hash160.

   me->get_hash_value_for_itab(
      EXPORTING
        it_table = gt_final_req
      IMPORTING
        ev_hash = lv_hash_old ).

   IF lv_hash_old <> lv_hash_new.
     “ There's a difference
   ENDIF.

 

cl_aunit_assert=>assert_equals(
      act = lv_hash
      exp = '5CAF285B3FA1C3F971BBEEEE1D61EF9095B6F4CC'
      msg = 'Hash value for output not correct'
    ).

Conclusion

When I use this I first validate that the internal table I wish to calculate hash for is correct. Then in debug I fetch the hash value for the internal table, and hardcode this into my assertion.

 

Assigned Tags

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

      Thanks for this nice utility. I'm not sure in which case it can be useful for the unit tests, but anyway it may be useful in a number of situations.

      Also, maybe using the following code is a little bit faster for serializing the internal table, but I don't know if it can be used for a later hash comparison:

      EXPORT itab = it_table TO DATA BUFFER lv_xstring COMPRESSION OFF.
      Author's profile photo Jan Braendgaard Petersen
      Jan Braendgaard Petersen
      Blog Post Author

      Hello Sandra, thank you for your comment. I will test this out. This would also make the code a lot more readable.

      BR, Jan.

      Author's profile photo Chairat (Par) Onyaem
      Chairat (Par) Onyaem

      Thanks for this. I’m looking for a way to get the hash value of the primary key to compare if two records are the same record and this will be very useful.

      Author's profile photo Robert Forster
      Robert Forster

      Hi,

      in unit test you can compare internal tables directly with assert_equals. I find the result better readable than some hast value which is different

      Author's profile photo Jan Braendgaard Petersen
      Jan Braendgaard Petersen
      Blog Post Author

      Hello Robert, thank you for your comment. My challenge here was that I did not have the two tables available at the same time, so I could not make the comparison directly with assert_equals method.

      BR, Jan

      Author's profile photo Hans-Dieter Loew
      Hans-Dieter Loew

      Hello together,

      the line

      EXPORT itab = it_table TO DATA BUFFER lv_xstring COMPRESSION OFF.

      will return different results in unicode and non-unicode systems/different releases.

      The only way that I found to get the same hash values in unicode and non-unicode system/different releases was the idea from Jan.

       

      BR Hans-Dieter

      Author's profile photo Constantinos Sourelis
      Constantinos Sourelis

      Thank you Jan! I needed a hash since internal tables cannot be part of a (containing) internal table's key.

      I wrote a test on Sandra's suggestion to use EXPORT - for a large table (130K rows). Seems much quicker, by a factor of about seven.

      Regarding Hans' comment on intra-system hash preservation, I can see how EXPORT's serialization can be dependent on the release, in general.
      However, wouldn't also the "loop serialization" end up with a different byte sequence between unicode and non-unicode systems (the former using two bytes per character)?

      Note the loop can be made faster by not iterating over the components and using instead

      ASSIGN COMPONENT 0 OF STRUCTURE ... CASTING TYPE x

      But, at least for a short structure like I used, the speedup isn't much.

      Another advantage of EXPORT is that it works for internal tables containing, e.g., string types (which would raise cx_sy_assign_cast_illegal_cast in the loop).

       

      Results from a system running kernel 753. (I am also testing cl_abap_message_digest=>calculate_hash_for_raw which allows for SHA-2 hashes):

      Internal%20table%20hash%20speed%20results

      Author's profile photo Jan Braendgaard Petersen
      Jan Braendgaard Petersen
      Blog Post Author

      Hello Constantinos Sourelis

      Thank you for the work on timing the different ways. I'm pretty sure that your thoughts on the difference between unicode and non-unicode byte sequence are correct. I haven't tested this out though, as I primarely use this hashing on the same system.

      BR, Jan

      Author's profile photo Bence Toth
      Bence Toth

      I needed a method for transaction SAAB Checkpoint Groups, to generate a SUBKEY that is the same for the same callstack during logging (To generate only a single log entry for the same callstack).

      For Dynamic Logpoints there is a similar function, called hash_stack( ):
      Dynamic Logpoints in ABAP

      Thanks to this method, I am able to implement the same functionality as hash_stack( )!