CLASS zcl_abapgit_object_tabl_compar DEFINITION PUBLIC CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_comparator . METHODS constructor IMPORTING !ii_local TYPE REF TO zif_abapgit_xml_input. PROTECTED SECTION. TYPES: ty_founds TYPE STANDARD TABLE OF rsfindlst WITH NON-UNIQUE DEFAULT KEY . TYPES: ty_seu_obj TYPE STANDARD TABLE OF seu_obj WITH NON-UNIQUE DEFAULT KEY . DATA mi_local TYPE REF TO zif_abapgit_xml_input. METHODS get_where_used_recursive IMPORTING !iv_object_name TYPE csequence !iv_depth TYPE i !iv_object_type TYPE euobj-id !it_scope TYPE ty_seu_obj RETURNING VALUE(rt_founds_all) TYPE ty_founds RAISING zcx_abapgit_exception . METHODS is_structure_used_in_db_table IMPORTING !iv_object_name TYPE dd02v-tabname RETURNING VALUE(rv_is_structure_used_in_db_tab) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS validate IMPORTING !ii_remote_version TYPE REF TO zif_abapgit_xml_input !ii_local_version TYPE REF TO zif_abapgit_xml_input !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rv_message) TYPE string RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS ZCL_ABAPGIT_OBJECT_TABL_COMPAR IMPLEMENTATION. METHOD constructor. mi_local = ii_local. ENDMETHOD. METHOD get_where_used_recursive. DATA: lt_findstrings TYPE string_table, lt_founds TYPE STANDARD TABLE OF rsfindlst, lt_scope TYPE ty_seu_obj, lv_findstring LIKE LINE OF lt_findstrings. FIELD-SYMBOLS: TYPE rsfindlst. IF iv_object_name IS INITIAL. RETURN. ENDIF. lt_scope = it_scope. lv_findstring = iv_object_name. INSERT lv_findstring INTO TABLE lt_findstrings. DO iv_depth TIMES. CLEAR: lt_founds. CALL FUNCTION 'RS_EU_CROSSREF' EXPORTING i_find_obj_cls = iv_object_type no_dialog = 'X' TABLES i_findstrings = lt_findstrings o_founds = lt_founds i_scope_object_cls = lt_scope EXCEPTIONS not_executed = 1 not_found = 2 illegal_object = 3 no_cross_for_this_object = 4 batch = 5 batchjob_error = 6 wrong_type = 7 object_not_exist = 8 OTHERS = 9. IF sy-subrc = 1 OR sy-subrc = 2 OR lines( lt_founds ) = 0. EXIT. ELSEIF sy-subrc > 2. zcx_abapgit_exception=>raise_t100( ). ENDIF. INSERT LINES OF lt_founds INTO TABLE rt_founds_all. CLEAR: lt_findstrings. LOOP AT lt_founds ASSIGNING . lv_findstring = -object. INSERT lv_findstring INTO TABLE lt_findstrings. ENDLOOP. ENDDO. ENDMETHOD. METHOD is_structure_used_in_db_table. DATA: lt_scope TYPE ty_seu_obj, lt_founds TYPE ty_founds. APPEND 'TABL' TO lt_scope. APPEND 'STRU' TO lt_scope. lt_founds = get_where_used_recursive( iv_object_name = iv_object_name iv_object_type = 'STRU' it_scope = lt_scope iv_depth = 5 ). DELETE lt_founds WHERE object_cls <> 'DT'. rv_is_structure_used_in_db_tab = boolc( lines( lt_founds ) > 0 ). ENDMETHOD. METHOD validate. DATA: lt_previous_table_fields TYPE TABLE OF dd03p, ls_previous_table_field LIKE LINE OF lt_previous_table_fields, lt_current_table_fields TYPE TABLE OF dd03p, ls_current_table_field LIKE LINE OF lt_current_table_fields, ls_dd02v TYPE dd02v, ls_item TYPE zif_abapgit_definitions=>ty_item, lv_inconsistent TYPE abap_bool. FIELD-SYMBOLS TYPE abap_bool. ii_remote_version->read( EXPORTING iv_name = 'DD02V' CHANGING cg_data = ls_dd02v ). " We only want to compare transparent tables, or structures used in transparent tables IF ls_dd02v-tabclass <> 'TRANSP' AND is_structure_used_in_db_table( ls_dd02v-tabname ) = abap_false. RETURN. ENDIF. " No comparison for global temporary tables ASSIGN COMPONENT 'IS_GTT' OF STRUCTURE ls_dd02v TO . IF sy-subrc = 0 AND = abap_true. RETURN. ENDIF. ii_remote_version->read( EXPORTING iv_name = 'DD03P_TABLE' CHANGING cg_data = lt_previous_table_fields ). ii_local_version->read( EXPORTING iv_name = 'DD03P_TABLE' CHANGING cg_data = lt_current_table_fields ). ls_item-obj_name = ls_dd02v-tabname. ls_item-obj_type = 'TABL'. LOOP AT lt_previous_table_fields INTO ls_previous_table_field. READ TABLE lt_current_table_fields WITH KEY fieldname = ls_previous_table_field-fieldname INTO ls_current_table_field. IF sy-subrc = 0. IF ls_current_table_field-rollname <> ls_previous_table_field-rollname. IF ls_current_table_field-rollname IS NOT INITIAL AND ls_previous_table_field-rollname IS NOT INITIAL. ii_log->add_info( iv_msg = |Field { ls_previous_table_field-fieldname }: | & |Data element changed from { ls_previous_table_field-rollname } | & |to { ls_current_table_field-rollname }| is_item = ls_item ). ELSEIF ls_current_table_field-rollname IS NOT INITIAL. ii_log->add_info( iv_msg = |Field { ls_previous_table_field-fieldname }: | & |Data type changed from internal type | & |{ ls_previous_table_field-inttype }(length { ls_previous_table_field-intlen }) | & |to data element { ls_current_table_field-rollname }| is_item = ls_item ). ELSEIF ls_previous_table_field-rollname IS NOT INITIAL. ii_log->add_info( iv_msg = |Field { ls_previous_table_field-fieldname }: | & |Data type changed from date element { ls_previous_table_field-rollname } | & |to internal type | & |{ ls_current_table_field-inttype }(length { ls_current_table_field-intlen })| is_item = ls_item ). ENDIF. "TODO: perform several other checks, e.g. field length truncated, ... lv_inconsistent = abap_true. ENDIF. ELSE. ii_log->add_info( iv_msg = |Field { ls_previous_table_field-fieldname } removed| is_item = ls_item ). lv_inconsistent = abap_true. ENDIF. ENDLOOP. IF lv_inconsistent = abap_true. rv_message = |Database Table { ls_dd02v-tabname }: Fields were changed. This may lead to inconsistencies!|. ENDIF. IF NOT rv_message IS INITIAL. rv_message = |Database Table { ls_dd02v-tabname }: { rv_message }|. ENDIF. ENDMETHOD. METHOD zif_abapgit_comparator~compare. rs_result-text = validate( ii_remote_version = ii_remote ii_local_version = mi_local ii_log = ii_log ). ENDMETHOD. ENDCLASS.