@@ -166,6 +166,54 @@ create or replace package body ut_compound_data_helper is
166166 select replace((extract(a_item_data,a_join_by_xpath).getclobval()),chr(10)) into l_pk_value from dual;
167167 return l_pk_value;
168168 end;
169+
170+ function get_rows_diff_by_sql(
171+ a_expected_dataset_guid raw, a_actual_dataset_guid raw, a_diff_id raw,
172+ a_max_rows integer, a_exclude_xpath varchar2, a_include_xpath varchar2,
173+ a_join_by_xpath varchar2
174+ ) return tt_row_diffs is
175+ l_column_filter varchar2(32767);
176+ l_results tt_row_diffs;
177+ begin
178+ l_column_filter := get_columns_row_filter(a_exclude_xpath,a_include_xpath);
179+
180+ execute immediate q'[with diff_info as
181+ ( select act_data_id, exp_data_id,
182+ act_item_data,exp_item_data, :join_by join_by, item_no
183+ from ut_compound_data_diff_tmp
184+ where diff_id = :diff_id ),
185+ exp as (
186+ select exp_item_data, exp_data_id, item_no rn,rownum col_no,
187+ nvl2(exp_item_data,ut3.ut_compound_data_helper.get_pk_value(i.join_by,exp_item_data),null) pk_value,
188+ s.column_value col, s.column_value.getRootElement() col_name, s.column_value.getclobval() col_val
189+ from diff_info i,
190+ table( xmlsequence( extract(i.exp_item_data,'/*/*') ) ) s
191+ where i.exp_data_id = :self_guid),
192+ act as (
193+ select act_item_data, act_data_id, item_no rn, rownum col_no,
194+ nvl2(act_item_data,ut3.ut_compound_data_helper.get_pk_value(i.join_by,act_item_data),null) pk_value,
195+ s.column_value col, s.column_value.getRootElement() col_name, s.column_value.getclobval() col_val
196+ from diff_info i,
197+ table( xmlsequence( extract(i.act_item_data,'/*/*') ) ) s
198+ where i.act_data_id = :other_guid)
199+ select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, pk_value pk_value
200+ from (
201+ select nvl(exp.rn, act.rn) rn, nvl(exp.pk_value, act.pk_value) pk_value, exp.col exp_item, act.col act_item
202+ from exp join act
203+ on exp.rn = act.rn and exp.col_name = act.col_name
204+ where dbms_lob.compare(exp.col_val, act.col_val) != 0)
205+ unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') )
206+ union all
207+ select item_no as rn, case when exp_data_id is null then 'Extra' else 'Missing' end as diff_type,
208+ xmlserialize(content nvl(exp_item_data, act_item_data) no indent) diffed_row,
209+ nvl2(i.join_by,ut3.ut_compound_data_helper.get_pk_value(i.join_by,coalesce(exp_item_data,act_item_data)),null) pk_value
210+ from diff_info i
211+ where act_data_id is null or exp_data_id is null]'
212+ bulk collect into l_results
213+ using a_join_by_xpath, a_diff_id, a_expected_dataset_guid,a_actual_dataset_guid;
214+
215+ return l_results;
216+ end;
169217
170218 function get_rows_diff(
171219 a_expected_dataset_guid raw, a_actual_dataset_guid raw, a_diff_id raw,
@@ -438,9 +486,11 @@ create or replace package body ut_compound_data_helper is
438486
439487 end;
440488
441- function compare_type(a_join_by_xpath in varchar2,a_unordered boolean) return varchar2 is
489+ function compare_type(a_join_by_xpath in varchar2,a_unordered boolean, a_is_sql_diffable integer := 0 ) return varchar2 is
442490 begin
443491 case
492+ when a_is_sql_diffable = 1 then
493+ return gc_compare_sql;
444494 when a_join_by_xpath is not null then
445495 return gc_compare_join_by;
446496 when a_unordered then
@@ -453,23 +503,26 @@ create or replace package body ut_compound_data_helper is
453503 function get_rows_diff(
454504 a_expected_dataset_guid raw, a_actual_dataset_guid raw, a_diff_id raw,
455505 a_max_rows integer, a_exclude_xpath varchar2, a_include_xpath varchar2,
456- a_join_by_xpath varchar2,a_unorderdered boolean
506+ a_join_by_xpath varchar2,a_unorderdered boolean, a_is_sql_diffable integer
457507 ) return tt_row_diffs is
458- l_results tt_row_diffs;
459- l_compare_type varchar2(10):= compare_type(a_join_by_xpath,a_unorderdered);
508+ l_result tt_row_diffs := tt_row_diffs() ;
509+ l_compare_type varchar2(10):= compare_type(a_join_by_xpath,a_unorderdered, a_is_sql_diffable );
460510 begin
461511 case
512+ when l_compare_type = gc_compare_sql then
513+ l_result := get_rows_diff_by_sql(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
514+ a_max_rows, a_exclude_xpath, a_include_xpath ,a_join_by_xpath);
462515 when l_compare_type = gc_compare_join_by then
463- return get_rows_diff(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
516+ l_result := get_rows_diff(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
464517 a_max_rows, a_exclude_xpath, a_include_xpath ,a_join_by_xpath);
465518 when l_compare_type = gc_compare_unordered then
466- return get_rows_diff_unordered(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
519+ l_result := get_rows_diff_unordered(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
467520 a_max_rows, a_exclude_xpath, a_include_xpath);
468521 else
469- return get_rows_diff(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
522+ l_result := get_rows_diff(a_expected_dataset_guid, a_actual_dataset_guid, a_diff_id,
470523 a_max_rows, a_exclude_xpath, a_include_xpath);
471524 end case;
472-
525+ return l_result;
473526 end;
474527
475528 function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is
@@ -565,7 +618,8 @@ create or replace package body ut_compound_data_helper is
565618 begin
566619 l_column_filter := ut_compound_data_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
567620 l_pk_hash_sql := get_column_pk_hash(a_join_by_xpath);
568-
621+
622+ --Use a item hash as pk hash for unordered
569623 execute immediate 'merge into ' || l_ut_owner || '.ut_compound_data_tmp tgt
570624 using (
571625 select ucd_out.item_hash,
@@ -690,6 +744,7 @@ create or replace package body ut_compound_data_helper is
690744 return l_sql;
691745 end;
692746
747+ -- TODO:Rebuild as the unordered can be done using join_by compare
693748 function get_refcursor_matcher_sql(a_owner in varchar2,a_inclusion_matcher boolean := false, a_negated_match boolean := false) return varchar2 is
694749 l_sql varchar2(32767);
695750 begin
@@ -704,6 +759,105 @@ create or replace package body ut_compound_data_helper is
704759
705760 return l_sql;
706761 end;
707-
762+
763+ function generate_xmltab_stmt (a_column_info xmltype) return varchar2 is
764+ l_sql_stmt varchar2(32767);
765+ begin
766+ for i in (select /*+ CARDINALITY(xt 100) */
767+ xt.name
768+ from (select a_column_info item_data from dual) x,
769+ xmltable(
770+ '/ROW/*'
771+ passing x.item_data
772+ columns
773+ name varchar2(4000) PATH '@xml_valid_name'
774+ ) xt)
775+ loop
776+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ',' end ||i.name||q'[ varchar2(4000) PATH ']'||i.name||q'[']';
777+ end loop;
778+ return l_sql_stmt;
779+ end;
780+
781+ function generate_equal_sql (a_column_info xmltype) return varchar2 is
782+ l_sql_stmt varchar2(32767);
783+ begin
784+ for i in (select /*+ CARDINALITY(xt 100) */
785+ xt.name
786+ from (select a_column_info item_data from dual) x,
787+ xmltable(
788+ '/ROW/*'
789+ passing x.item_data
790+ columns
791+ name varchar2(4000) PATH '@xml_valid_name'
792+ ) xt)
793+ loop
794+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' and ' end ||' a.'||i.name||q'[ = ]'||' e.'||i.name;
795+ end loop;
796+ return l_sql_stmt;
797+ end;
798+
799+ function generate_not_equal_sql (a_column_info xmltype, a_join_by_xpath varchar2) return varchar2 is
800+ l_sql_stmt varchar2(32767);
801+ l_pk_xpath_tabs ut_varchar2_list := ut_varchar2_list();
802+ begin
803+ l_pk_xpath_tabs := ut_utils.string_to_table(a_join_by_xpath,'|');
804+
805+ for i in (
806+ with xpaths_tab as (select column_value xpath from table(l_pk_xpath_tabs)),
807+ pk_names as (select REGEXP_SUBSTR (xpath,'[^(/\*/)](.+)$') name
808+ from xpaths_tab)
809+ select /*+ CARDINALITY(xt 100) */
810+ xt.name
811+ from (select a_column_info item_data from dual) x,
812+ xmltable(
813+ '/ROW/*'
814+ passing x.item_data
815+ columns
816+ name varchar2(4000) PATH '@xml_valid_name'
817+ ) xt
818+ where not exists (select 1 from pk_names p where lower(p.name) = lower(xt.name))
819+ )
820+ loop
821+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' or ' end ||' a.'||i.name||q'[ <> ]'||' e.'||i.name;
822+ end loop;
823+ return l_sql_stmt;
824+ end;
825+
826+ function generate_join_by_on_stmt (a_column_info xmltype, a_join_by_xpath varchar2) return varchar2 is
827+ l_sql_stmt varchar2(32767);
828+ l_pk_xpath_tabs ut_varchar2_list := ut_varchar2_list();
829+
830+ begin
831+ l_pk_xpath_tabs := ut_utils.string_to_table(a_join_by_xpath,'|');
832+
833+ for i in (with xpaths_tab as (select column_value xpath from table(l_pk_xpath_tabs))
834+ select REGEXP_SUBSTR (xpath,'[^(/\*/)](.+)$') name
835+ from xpaths_tab)
836+ loop
837+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' and ' end ||' a.'||i.name||q'[ = ]'||' e.'||i.name;
838+ end loop;
839+ return l_sql_stmt;
840+ end;
841+
842+ function generate_join_null_sql (a_column_info xmltype, a_join_by_xpath varchar2) return varchar2 is
843+ l_sql_stmt varchar2(32767);
844+ l_pk_xpath_tabs ut_varchar2_list := ut_varchar2_list();
845+
846+ begin
847+ l_pk_xpath_tabs := ut_utils.string_to_table(a_join_by_xpath,'|');
848+
849+ for i in (with xpaths_tab as (select column_value xpath from table(l_pk_xpath_tabs))
850+ select REGEXP_SUBSTR (xpath,'[^(/\*/)](.+)$') name
851+ from xpaths_tab)
852+ loop
853+ l_sql_stmt := l_sql_stmt || case
854+ when l_sql_stmt is null
855+ then null
856+ else ' or '
857+ end ||' a.'||i.name||q'[ is null or ]'||' e.'||i.name||q'[ is null]';
858+ end loop;
859+ return l_sql_stmt;
860+ end;
861+
708862end;
709863/
0 commit comments