@@ -85,81 +85,45 @@ create or replace package body ut_compound_data_helper is
8585 end if;
8686 return l_filter;
8787 end;
88-
89- function get_columns_diff(
90- a_expected xmltype, a_actual xmltype, a_exclude_xpath varchar2, a_include_xpath varchar2
91- ) return tt_column_diffs is
88+
89+ function get_columns_diff(a_expected ut_cursor_column_tab, a_actual ut_cursor_column_tab)
90+ return tt_column_diffs is
9291 l_column_filter varchar2(32767);
9392 l_sql varchar2(32767);
9493 l_results tt_column_diffs;
9594 begin
96- l_column_filter := get_columns_row_filter(a_exclude_xpath, a_include_xpath);
97- --CARDINALITY hints added to address issue: https://github.com/utPLSQL/utPLSQL/issues/752
98- l_sql := q'[
99- with
100- expected_cols as ( select :a_expected as item_data from dual ),
101- actual_cols as ( select :a_actual as item_data from dual ),
102- expected_cols_info as (
103- select e.*,
104- replace(expected_type,'VARCHAR2','CHAR') expected_type_compare
105- from (
106- select /*+ CARDINALITY(xt 100) */
107- rownum expected_pos,
108- xt.name expected_name,
109- xt.type expected_type
110- from (select ]'||l_column_filter||q'[ from expected_cols ucd) x,
111- xmltable(
112- '/ROW/*'
113- passing x.item_data
114- columns
115- name varchar2(4000) PATH '@xml_valid_name',
116- type varchar2(4000) PATH '/'
117- ) xt
118- ) e
119- ),
120- actual_cols_info as (
121- select a.*,
122- replace(actual_type,'VARCHAR2','CHAR') actual_type_compare
123- from (select /*+ CARDINALITY(xt 100) */
124- rownum actual_pos,
125- xt.name actual_name,
126- xt.type actual_type
127- from (select ]'||l_column_filter||q'[ from actual_cols ucd) x,
128- xmltable('/ROW/*'
129- passing x.item_data
130- columns
131- name varchar2(4000) path '@xml_valid_name',
132- type varchar2(4000) path '/'
133- ) xt
134- ) a
135- ),
136- joined_cols as (
137- select e.*, a.*,
138- row_number() over(partition by case when actual_pos + expected_pos is not null then 1 end order by actual_pos) a_pos_nn,
139- row_number() over(partition by case when actual_pos + expected_pos is not null then 1 end order by expected_pos) e_pos_nn
140- from expected_cols_info e
141- full outer join actual_cols_info a on e.expected_name = a.actual_name
142- )
95+ with
96+ expected_cols as
97+ (select access_path exp_column_name,column_position exp_col_pos,
98+ replace(column_type,'VARCHAR2','CHAR') exp_col_type_compare, column_type exp_col_type
99+ from table(a_expected)),
100+ actual_cols as
101+ (select access_path act_column_name,column_position act_col_pos,
102+ replace(column_type,'VARCHAR2','CHAR') act_col_type_compare, column_type act_col_type
103+ from table(a_actual)),
104+ joined_cols as
105+ (select e.*,a.*,
106+ row_number() over(partition by case when a.act_col_pos + e.exp_col_pos is not null then 1 end order by a.act_col_pos) a_pos_nn,
107+ row_number() over(partition by case when a.act_col_pos + e.exp_col_pos is not null then 1 end order by e.exp_col_pos) e_pos_nn
108+ from expected_cols e
109+ full outer join actual_cols a on e.exp_column_name = a.act_column_name)
143110 select case
144- when expected_pos is null and actual_pos is not null then '+'
145- when expected_pos is not null and actual_pos is null then '-'
146- when expected_type_compare != actual_type_compare then 't'
111+ when exp_col_pos is null and act_col_pos is not null then '+'
112+ when exp_col_pos is not null and act_col_pos is null then '-'
113+ when exp_col_type_compare != act_col_type_compare then 't'
147114 else 'p'
148115 end as diff_type,
149- expected_name, expected_type, expected_pos,
150- actual_name, actual_type, actual_pos
116+ exp_column_name, exp_col_type, exp_col_pos,
117+ act_column_name, act_col_type, act_col_pos
118+ bulk collect into l_results
151119 from joined_cols
152120 --column is unexpected (extra) or missing
153- where actual_pos is null or expected_pos is null
121+ where act_col_pos is null or exp_col_pos is null
154122 --column type is not matching (except CHAR/VARCHAR2)
155- or actual_type_compare != expected_type_compare
123+ or act_col_type_compare != exp_col_type_compare
156124 --column position is not matching (both when excluded extra/missing columns as well as when they are included)
157- or (a_pos_nn != e_pos_nn and expected_pos != actual_pos)
158- order by expected_pos, actual_pos]';
159- execute immediate l_sql
160- bulk collect into l_results
161- using a_expected, a_actual, a_exclude_xpath, a_include_xpath, a_exclude_xpath, a_include_xpath;
162-
125+ or (a_pos_nn != e_pos_nn and exp_col_pos != act_col_pos)
126+ order by exp_col_pos, act_col_pos;
163127 return l_results;
164128 end;
165129
@@ -690,6 +654,7 @@ create or replace package body ut_compound_data_helper is
690654 end if;
691655 ut_utils.append_to_clob(l_compare_sql,l_temp_string);
692656
657+ dbms_output.put_line(l_compare_sql);
693658 return l_compare_sql;
694659 end;
695660
@@ -735,12 +700,8 @@ create or replace package body ut_compound_data_helper is
735700 select lev,column_name,parent_name from hier),
736701 t1(column_name, parent_name) AS (
737702 select column_name,parent_name from table(:a_cursor_info) where parent_name is null
738- union all
739- select t2.column_name,t2.parent_name from table(:a_cursor_info) t2, t1 where t2.parent_name = t1.column_name)
740- select ut_cursor_column(i.column_name,i.column_schema,i.column_type_name, i.column_prec,i.column_scale,i.column_len, i.parent_name,
741- i.hierarchy_level,i.column_position, i.column_type)
742- from t1 join table(:a_cursor_info) i on ( nvl(t1.parent_name,1) = nvl(i.parent_name,1) and t1.column_name = i.column_name)
743- ]';
703+ union all
704+ select t2.column_name,t2.parent_name from table(:a_cursor_info) t2, t1 where t2.parent_name = t1.column_name)]';
744705 begin
745706 return l_sql;
746707 end;
@@ -750,10 +711,13 @@ create or replace package body ut_compound_data_helper is
750711 l_sql varchar2(32767) := get_cursor_vs_list_sql;
751712 l_result ut_cursor_column_tab := ut_cursor_column_tab();
752713 begin
714+ l_sql := l_sql || q'[select ut_cursor_column(i.column_name,i.column_schema,i.column_type_name, i.column_prec,i.column_scale,i.column_len, i.parent_name,
715+ i.hierarchy_level,i.column_position, i.column_type)
716+ from t1 join table(:a_cursor_info) i on ( nvl(t1.parent_name,1) = nvl(i.parent_name,1) and t1.column_name = i.column_name)]';
753717 if a_include then
754718 l_sql := l_sql || ' join constructed c on ( nvl(t1.parent_name,1) = nvl(c.parent_name,1) and t1.column_name = c.column_name)';
755719 else
756- l_sql := l_sql ||'left outer join constructed c on ( nvl(t1.parent_name,1) = nvl(c.parent_name,1) and t1.column_name = c.column_name)
720+ l_sql := l_sql ||' left outer join constructed c on ( nvl(t1.parent_name,1) = nvl(c.parent_name,1) and t1.column_name = c.column_name)
757721 where c.column_name is null';
758722 end if;
759723
@@ -764,18 +728,35 @@ create or replace package body ut_compound_data_helper is
764728 end;
765729
766730 function compare_cursor_to_columns(a_cursor_info ut_cursor_column_tab, a_current_list ut_varchar2_list)
767- return ut_cursor_column_tab is
731+ return ut_varchar2_list is
768732 l_sql varchar2(32767) := get_cursor_vs_list_sql;
769- l_result ut_cursor_column_tab := ut_cursor_column_tab ();
733+ l_result ut_varchar2_list := ut_varchar2_list ();
770734 begin
735+ l_sql := l_sql || q'[select c.parent_name || case when c.parent_name is null then null else '/' end ||c.column_name
736+ from t1 join table(:a_cursor_info) i on ( nvl(t1.parent_name,1) = nvl(i.parent_name,1) and t1.column_name = i.column_name)]';
771737 l_sql := l_sql ||'right outer join constructed c on ( nvl(t1.parent_name,1) = nvl(c.parent_name,1) and t1.column_name = c.column_name)
772738 where t1.column_name is null';
773739
774740 execute immediate l_sql bulk collect into l_result
775741 using a_current_list,a_cursor_info,a_cursor_info,a_cursor_info;
776742 return l_result;
777743 end;
778-
744+
745+ function get_missing_pk(a_expected ut_cursor_column_tab, a_actual ut_cursor_column_tab, a_current_list ut_varchar2_list)
746+ return tt_missing_pk is
747+ l_actual ut_varchar2_list := coalesce(compare_cursor_to_columns(a_actual,a_current_list),ut_varchar2_list());
748+ l_expected ut_varchar2_list := coalesce(compare_cursor_to_columns(a_expected,a_current_list),ut_varchar2_list());
749+ l_missing_pk tt_missing_pk;
750+ begin
751+ select name,type
752+ bulk collect into l_missing_pk
753+ from
754+ (select act.column_value name, 'e' type from table(l_expected) act
755+ union all
756+ select exp.column_value name, 'a' type from table(l_actual) exp)
757+ order by type desc,name;
758+ return l_missing_pk;
759+ end;
779760
780761 function inc_exc_columns_from_cursor (a_cursor_info ut_cursor_column_tab, a_exclude_xpath ut_varchar2_list, a_include_xpath ut_varchar2_list)
781762 return ut_cursor_column_tab is
@@ -819,5 +800,13 @@ create or replace package body ut_compound_data_helper is
819800 return l_result;
820801 end;
821802
803+ function contains_collection (a_cursor_info ut_cursor_column_tab) return number is
804+ l_collection_elements number;
805+ begin
806+ select count(1) into l_collection_elements from
807+ table(a_cursor_info) c where c.is_collection = 1;
808+ return l_collection_elements;
809+ end;
810+
822811end;
823812/
0 commit comments