@@ -25,16 +25,15 @@ create or replace package body ut_compound_data_helper is
2525 l_data ut_data_value := a_column_details.value;
2626 l_key varchar2(4000) := ut_utils.xmlgen_escaped_string(a_column_details.KEY);
2727 l_is_diff number;
28- begin
28+ begin
2929 l_result := '<'||l_key||' xml_valid_name="'||l_key;
3030 if l_data is of(ut_data_value_xmltype) then
31- l_result := l_result||'" sql_diffable="0">' || (treat(l_data as ut_data_value_xmltype).to_string);
31+ l_result := l_result||'" sql_diffable="0" user_defined="1" >' ||trim( both '''' from (treat(l_data as ut_data_value_xmltype).to_string) );
3232 else
3333 l_is_diff := ut_curr_usr_compound_helper.is_sql_compare_int((treat(l_data as ut_data_value_varchar2).data_value));
34- l_result := l_result||'" sql_diffable="'||l_is_diff||'">' || ut_utils.xmlgen_escaped_string((treat(l_data as ut_data_value_varchar2).data_value));
34+ l_result := l_result||'" sql_diffable="'||l_is_diff||'" user_defined="0" >' || ut_utils.xmlgen_escaped_string((treat(l_data as ut_data_value_varchar2).data_value));
3535 end if;
36-
37- l_result := l_result ||'</'||l_key||'>';
36+ l_result := l_result ||'</'||l_key||'>';
3837 return xmltype(l_result);
3938 end;
4039
@@ -202,6 +201,8 @@ create or replace package body ut_compound_data_helper is
202201 from diff_info i,
203202 table( xmlsequence( extract(i.act_item_data,'/*/*') ) ) s
204203 where i.act_data_id = :other_guid)
204+ select rn, diff_type, diffed_row, pk_value pk_value
205+ from (
205206 select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, pk_value pk_value
206207 from (
207208 select nvl(exp.rn, act.rn) rn, nvl(exp.pk_value, act.pk_value) pk_value, exp.col exp_item, act.col act_item
@@ -215,7 +216,8 @@ create or replace package body ut_compound_data_helper is
215216 nvl2(i.join_by,ut3.ut_compound_data_helper.get_pk_value(i.join_by,case when exp_data_id is null then act_item_data else exp_item_data end),null) pk_value
216217 from diff_info i
217218 where act_data_id is null or exp_data_id is null
218- order by 1 , 2]'
219+ )
220+ order by 2, 1]'
219221 bulk collect into l_results
220222 using a_exclude_xpath,a_include_xpath,
221223 a_exclude_xpath,a_include_xpath,
@@ -359,7 +361,7 @@ create or replace package body ut_compound_data_helper is
359361 if a_join_by_xpath is not null then
360362 l_pk_xpath_tabs := ut_utils.string_to_table(a_join_by_xpath,'|');
361363 l_column_filter := get_columns_row_filter(a_exclude_xpath, a_include_xpath);
362-
364+
363365 execute immediate q'[
364366 with xpaths_tab as (select column_value xpath from table(:xpath_tabs)),
365367 expected_column_info as ( select :expected as item_data from dual ),
@@ -390,84 +392,7 @@ create or replace package body ut_compound_data_helper is
390392
391393 return l_no_missing_keys;
392394 end;
393-
394- function get_inclusion_matcher_sql(a_owner in varchar2) return varchar2 is
395- l_sql varchar2(32767);
396- begin
397- l_sql := 'with source_data as
398- ( select t.data_id,t.item_hash,t.duplicate_no,
399- pk_hash
400- from ' || a_owner || '.ut_compound_data_tmp t
401- where data_id = :self_guid or data_id = :other_guid
402- )
403- select distinct :diff_id,tmp.item_hash,tmp.pk_hash,tmp.duplicate_no
404- from(
405- (
406- select t.item_hash,t. duplicate_no,t.pk_hash
407- from source_data t
408- where t.data_id = :self_guid
409- minus
410- select t.item_hash,t. duplicate_no,t.pk_hash
411- from source_data t
412- where t.data_id = :other_guid
413- )
414- union all
415- (
416- select t.item_hash,t. duplicate_no,t.pk_hash
417- from source_data t,
418- source_data s
419- where t.data_id = :other_guid
420- and s.data_id = :self_guid
421- and t.pk_hash = s.pk_hash
422- and t.item_hash != s.item_hash
423- )
424- )
425- tmp';
426- return l_sql;
427- end;
428-
429- function get_not_inclusion_matcher_sql(a_owner in varchar2) return varchar2 is
430- l_sql varchar2(32767);
431- begin
432- /* Self set does not contain any values from other set */
433- l_sql := 'with source_data as
434- ( select t.data_id,t.item_hash,t.duplicate_no,
435- pk_hash
436- from ' || a_owner || '.ut_compound_data_tmp t
437- where data_id = :self_guid or data_id = :other_guid
438- )
439- select distinct :diff_id,tmp.item_hash,tmp.pk_hash,tmp.duplicate_no
440- from
441- (
442- select act.item_hash,act. duplicate_no,act.pk_hash
443- from source_data act where act.data_id = :self_guid
444- and exists ( select 1
445- from source_data exp
446- where exp.data_id = :other_guid
447- and exp.item_hash = act.item_hash
448- )
449- union all
450- select null,null,null
451- from dual where :other_guid = :self_guid
452- )
453- tmp';
454- return l_sql;
455- end;
456-
457- -- TODO:Rebuild as the unordered can be done using join_by compare
458- function get_refcursor_matcher_sql(a_owner in varchar2,a_inclusion_matcher boolean := false, a_negated_match boolean := false) return varchar2 is
459- l_sql varchar2(32767);
460- begin
461- l_sql := 'insert into ' || a_owner || '.ut_compound_data_diff_tmp ( diff_id,item_hash,pk_hash,duplicate_no)'||chr(10);
462- if a_inclusion_matcher and not(a_negated_match) then
463- l_sql := l_sql || get_inclusion_matcher_sql(a_owner);
464- elsif a_inclusion_matcher and a_negated_match then
465- l_sql := l_sql || get_not_inclusion_matcher_sql(a_owner);
466- end if;
467-
468- return l_sql;
469- end;
470-
395+
471396 function generate_select_stmt(a_column_info ut_varchar2_list,a_xml_column_info xmltype) return clob is
472397 l_sql_stmt clob;
473398 l_col_type varchar2(4000);
@@ -570,19 +495,41 @@ create or replace package body ut_compound_data_helper is
570495 for i in 1..a_column_info.count loop
571496 l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' and ' end ||' a.'||a_column_info(i)||q'[ = ]'||' e.'||a_column_info(i);
572497 end loop;
573-
574498 return l_sql_stmt;
575499 end;
576500
577- function generate_join_by_on_stmt (a_join_by_xpath_tab ut_varchar2_list) return clob is
501+ function generate_join_by_on_stmt (a_join_by_xpath_tab ut_varchar2_list, a_columns_info xmltype, a_join_by_xpath in varchar2 ) return clob is
578502 l_sql_stmt clob;
579- begin
580- for i in (with xpaths_tab as (select column_value xpath from table(a_join_by_xpath_tab))
581- select REGEXP_SUBSTR (xpath,'[^(/\*/)](.+)$') name
582- from xpaths_tab)
503+ l_non_diff_var varchar2(32767);
504+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
505+ begin
506+ for i in (select /*+ CARDINALITY(xt 100) */
507+ distinct
508+ t.column_value,
509+ xt.is_sql_diff
510+ from
511+ (select a_columns_info item_data from dual) x,
512+ xmltable(
513+ '/ROW/*'
514+ passing x.item_data
515+ columns
516+ name varchar2(4000) PATH '@xml_valid_name',
517+ is_sql_diff varchar2(4000) PATH '@sql_diffable'
518+ ) xt,
519+ table(a_join_by_xpath_tab) t
520+ where xt.name = t.column_value)
583521 loop
584- 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;
522+
523+ if i.is_sql_diff = 0 then
524+ l_non_diff_var := l_ut_owner ||'.ut_compound_data_helper.get_hash((extract( a.'||i.column_value||','||a_join_by_xpath||')).getclobval()) = '
525+ ||l_ut_owner ||'.ut_compound_data_helper.get_hash((extract( e.'||i.column_value||','||a_join_by_xpath||')).getclobval())';
526+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' and ' end ||' a.'||i.column_value||q'[ = ]'||' e.'||i.column_value;
527+ elsif i.is_sql_diff = 1 then
528+ l_sql_stmt := l_sql_stmt || case when l_sql_stmt is null then null else ' and ' end ||' a.'||i.column_value||q'[ = ]'||' e.'||i.column_value;
529+ end if;
530+
585531 end loop;
532+
586533 return l_sql_stmt;
587534 end;
588535
@@ -605,7 +552,7 @@ create or replace package body ut_compound_data_helper is
605552 end;
606553
607554 function gen_compare_sql(a_column_info xmltype, a_exclude_xpath varchar2,
608- a_include_xpath varchar2, a_join_by_xpath varchar2) return clob is
555+ a_include_xpath varchar2, a_join_by_xpath varchar2, a_inclusion_type boolean, a_is_negated boolean ) return clob is
609556 l_compare_sql clob;
610557 l_temp_string varchar2(32767);
611558
@@ -621,7 +568,7 @@ create or replace package body ut_compound_data_helper is
621568 function get_columns_names (a_xpath_tab in ut_varchar2_list) return ut_varchar2_list is
622569 l_names_tab ut_varchar2_list := ut_varchar2_list();
623570 begin
624- select distinct REGEXP_SUBSTR (column_value,'[^(/\* /)](.+)$' )
571+ select distinct REGEXP_SUBSTR (column_value,'[^(\/*\ /)]+',1,1 )
625572 bulk collect into l_names_tab
626573 from table(a_xpath_tab);
627574 return l_names_tab;
@@ -642,12 +589,23 @@ create or replace package body ut_compound_data_helper is
642589 ) xt;
643590 return l_columns_info;
644591 end;
645-
592+
593+ function get_join_type(a_inclusion_compare in boolean,a_negated in boolean) return varchar2 is
594+ begin
595+ if a_inclusion_compare and not(a_negated) then
596+ return ' right outer join ';
597+ elsif a_inclusion_compare and a_negated then
598+ return ' inner join ';
599+ else
600+ return ' full outer join ';
601+ end if;
602+ end;
603+
646604 begin
647605 dbms_lob.createtemporary(l_compare_sql, true);
648606
607+ --TODO: Resolve issues with collection and nested tables, can we extract by internal column name if defined e.g. xml of colval.id.getclobval()
649608 --Check include and exclude columns and create an actual column list that have to be compared.
650- --TODO :Reformat
651609 if a_include_xpath is null and a_exclude_xpath is null then
652610 l_act_col_tab := get_columns_info(a_column_info);
653611 elsif a_include_xpath is not null and a_exclude_xpath is null then
@@ -697,28 +655,34 @@ create or replace package body ut_compound_data_helper is
697655 -- If no key defined do the join on all columns
698656 l_temp_string := ' select a.item_data as act_item_data, a.data_id act_data_id,'
699657 ||'e.item_data as exp_item_data, e.data_id exp_data_id, rownum item_no, nvl(e.dup_no,a.dup_no) dup_no '
700- ||'from act a full outer join exp e on ( ';
658+ ||'from act a '||get_join_type(a_inclusion_type,a_is_negated)||' exp e on ( ';
701659 ut_utils.append_to_clob(l_compare_sql,l_temp_string);
702- ut_utils.append_to_clob(l_compare_sql,generate_equal_sql(l_act_col_tab)||q'[ and e.dup_no = a.dup_no ) where a.data_id is null or e.data_id is null ]');
660+ ut_utils.append_to_clob(l_compare_sql,generate_equal_sql(l_act_col_tab)||q'[ and e.dup_no = a.dup_no ) where ]');
703661 else
704662 -- If key defined do the join or these and where on diffrences
705663 l_temp_string := q'[ select a.item_data act_item_data, a.data_id act_data_id, ]'
706- ||' e.item_data exp_item_data, e.data_id exp_data_id, rownum item_no,nvl(e.dup_no,a.dup_no) dup_no from act a full outer join exp e on ( e.dup_no = a.dup_no and ';
664+ ||' e.item_data exp_item_data, e.data_id exp_data_id, rownum item_no,nvl(e.dup_no,a.dup_no) dup_no from act a '||get_join_type(a_inclusion_type,a_is_negated)||' exp e on ( e.dup_no = a.dup_no and ';
707665 ut_utils.append_to_clob(l_compare_sql,l_temp_string);
708666
709- ut_utils.append_to_clob(l_compare_sql,generate_join_by_on_stmt (l_pk_xpath_tabs)||' ) ');
667+ ut_utils.append_to_clob(l_compare_sql,generate_join_by_on_stmt (l_pk_xpath_tabs,a_column_info,a_join_by_xpath )||' ) where ');
710668
711- l_where_stmt := generate_not_equal_sql(l_act_col_tab, l_pk_xpath_tabs);
712- case
713- when l_where_stmt is null then
714- ut_utils.append_to_clob(l_compare_sql,' where a.data_id is null or e.data_id is null');
715- else
716- ut_utils.append_to_clob(l_compare_sql,' where ( '||l_where_stmt||' ) or ( a.data_id is null or e.data_id is null )');
717- end case ;
669+
670+ if not a_is_negated then
671+ l_where_stmt := generate_not_equal_sql(l_act_col_tab, l_pk_xpath_tabs);
672+ if l_where_stmt is not null then
673+ ut_utils.append_to_clob(l_compare_sql,' ( '||l_where_stmt||' ) or ');
674+ end if;
675+ end if ;
718676 end if;
719-
720- --TEST
721- dbms_output.put_line( l_compare_sql);
677+
678+ --If its inlcusion we expect a actual set to fully match and have no extra elements over expected
679+ if a_inclusion_type and not(a_is_negated) then
680+ l_temp_string := ' ( a.data_id is null ) ';
681+ else
682+ l_temp_string := ' (a.data_id is null or e.data_id is null) ';
683+ end if;
684+ ut_utils.append_to_clob(l_compare_sql,l_temp_string);
685+
722686 return l_compare_sql;
723687 end;
724688
0 commit comments