Skip to content

Commit aaf4a3a

Browse files
committed
Adding ability to diff on collections and objects.
1 parent 442af33 commit aaf4a3a

23 files changed

+410
-295
lines changed

source/core/ut_utils.pkb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab,
400400

401401
procedure cleanup_temp_tables is
402402
begin
403-
execute immediate 'delete from ut_data_set_tmp';
404-
execute immediate 'delete from ut_data_set_diff_tmp';
403+
execute immediate 'delete from ut_compound_data_tmp';
404+
execute immediate 'delete from ut_compound_data_diff_tmp';
405405
end;
406406

407407
function to_version(a_version_no varchar2) return t_version is

source/create_synonyms_and_grants_for_public.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ grant execute on &&ut3_owner..ut_file_mapping to public;
6666
grant execute on &&ut3_owner..ut_file_mapper to public;
6767
grant execute on &&ut3_owner..ut_key_value_pairs to public;
6868
grant execute on &&ut3_owner..ut_key_value_pair to public;
69-
grant select, insert, delete on &&ut3_owner..ut_data_set_tmp to public;
70-
grant select, insert, delete on &&ut3_owner..ut_data_set_diff_tmp to public;
69+
grant select, insert, delete on &&ut3_owner..ut_compound_data_tmp to public;
70+
grant select, insert, delete on &&ut3_owner..ut_compound_data_diff_tmp to public;
7171
grant execute on &&ut3_owner..ut_sonar_test_reporter to public;
7272
grant execute on &&ut3_owner..ut_annotations to public;
7373
grant execute on &&ut3_owner..ut_annotation to public;
@@ -119,6 +119,6 @@ create public synonym ut_file_mapping for &&ut3_owner..ut_file_mapping;
119119
create public synonym ut_file_mapper for &&ut3_owner..ut_file_mapper;
120120
create public synonym ut_key_value_pairs for &&ut3_owner..ut_key_value_pairs;
121121
create public synonym ut_key_value_pair for &&ut3_owner..ut_key_value_pair;
122-
create public synonym ut_data_set_tmp for &&ut3_owner..ut_data_set_tmp;
123-
create public synonym ut_data_set_diff_tmp for &&ut3_owner..ut_data_set_diff_tmp;
122+
create public synonym ut_compound_data_tmp for &&ut3_owner..ut_compound_data_tmp;
123+
create public synonym ut_compound_data_diff_tmp for &&ut3_owner..ut_compound_data_diff_tmp;
124124
create public synonym ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter;

source/create_synonyms_and_grants_for_user.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ grant execute on &&ut3_owner..ut_file_mapping to &ut3_user;
8686
grant execute on &&ut3_owner..ut_file_mapper to &ut3_user;
8787
grant execute on &&ut3_owner..ut_key_value_pairs to &ut3_user;
8888
grant execute on &&ut3_owner..ut_key_value_pair to &ut3_user;
89-
grant select, insert, delete on &&ut3_owner..ut_data_set_tmp to &ut3_user;
90-
grant select, insert, delete on &&ut3_owner..ut_data_set_diff_tmp to &ut3_user;
89+
grant select, insert, delete on &&ut3_owner..ut_compound_data_tmp to &ut3_user;
90+
grant select, insert, delete on &&ut3_owner..ut_compound_data_diff_tmp to &ut3_user;
9191
grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user;
9292
grant execute on &&ut3_owner..ut_annotations to &ut3_user;
9393
grant execute on &&ut3_owner..ut_annotation to &ut3_user;
@@ -138,6 +138,6 @@ create or replace synonym &ut3_user..ut_file_mapping for &&ut3_owner..ut_file_ma
138138
create or replace synonym &ut3_user..ut_file_mapper for &&ut3_owner..ut_file_mapper;
139139
create or replace synonym &ut3_user..ut_key_value_pairs for &&ut3_owner..ut_key_value_pairs;
140140
create or replace synonym &ut3_user..ut_key_value_pair for &&ut3_owner..ut_key_value_pair;
141-
create or replace synonym &ut3_user..ut_data_set_tmp for &&ut3_owner..ut_cursor_data;
142-
create or replace synonym &ut3_user..ut_data_set_diff_tmp for &&ut3_owner..ut_data_set_diff_tmp;
141+
create or replace synonym &ut3_user..ut_compound_data_tmp for &&ut3_owner..ut_cursor_data;
142+
create or replace synonym &ut3_user..ut_compound_data_diff_tmp for &&ut3_owner..ut_compound_data_diff_tmp;
143143
create or replace synonym &ut3_user..ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter;

source/expectations/data_values/ut_data_set_diff_tmp.sql renamed to source/expectations/data_values/ut_compound_data_diff_tmp.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create global temporary table ut_data_set_diff_tmp(
1+
create global temporary table ut_compound_data_diff_tmp(
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -14,5 +14,5 @@ create global temporary table ut_data_set_diff_tmp(
1414
*/
1515
diff_id raw(16),
1616
item_no integer,
17-
constraint ut_data_set_diff_tmp_pk primary key(diff_id,item_no)
17+
constraint ut_compound_data_diff_tmp_pk primary key(diff_id,item_no)
1818
) on commit preserve rows;

source/expectations/data_values/ut_refcursor_helper.pkb renamed to source/expectations/data_values/ut_compound_data_helper.pkb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace package body ut_refcursor_helper is
1+
create or replace package body ut_compound_data_helper is
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -89,7 +89,7 @@ create or replace package body ut_refcursor_helper is
8989
l_sql varchar2(32767);
9090
l_results tt_column_diffs;
9191
begin
92-
l_column_filter := ut_refcursor_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
92+
l_column_filter := ut_compound_data_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
9393
l_sql := q'[
9494
with
9595
expected_cols as ( select :a_expected as item_data from dual ),
@@ -142,7 +142,7 @@ create or replace package body ut_refcursor_helper is
142142
l_column_filter := get_columns_filter(a_exclude_xpath, a_include_xpath);
143143
execute immediate q'[
144144
with
145-
diff_info as (select item_no from ut_data_set_diff_tmp ucdc where diff_id = :diff_guid and rownum <= :max_rows)
145+
diff_info as (select item_no from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid and rownum <= :max_rows)
146146
select *
147147
from (select rn, diff_type, xmlserialize(content data_item no indent) diffed_row
148148
from (select nvl(exp.rn, act.rn) rn,
@@ -152,8 +152,8 @@ create or replace package body ut_refcursor_helper is
152152
s.column_value.getRootElement() col_name,
153153
s.column_value.getclobval() col_val
154154
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
155-
from ut_data_set_tmp ucd
156-
where ucd.data_set_guid = :self_guid
155+
from ut_compound_data_tmp ucd
156+
where ucd.data_id = :self_guid
157157
and ucd.item_no in (select i.item_no from diff_info i)
158158
) r,
159159
table( xmlsequence( extract(r.item_data,'/*/*') ) ) s
@@ -163,8 +163,8 @@ create or replace package body ut_refcursor_helper is
163163
s.column_value.getRootElement() col_name,
164164
s.column_value.getclobval() col_val
165165
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
166-
from ut_data_set_tmp ucd
167-
where ucd.data_set_guid = :other_guid
166+
from ut_compound_data_tmp ucd
167+
where ucd.data_id = :other_guid
168168
and ucd.item_no in (select i.item_no from diff_info i)
169169
) r,
170170
table( xmlsequence( extract(r.item_data,'/*/*') ) ) s
@@ -180,14 +180,14 @@ create or replace package body ut_refcursor_helper is
180180
case when exp.item_no is null then 'Extra:' else 'Missing:' end as diff_type,
181181
xmlserialize(content nvl(exp.item_data, act.item_data) no indent) diffed_row
182182
from (select ucd.item_no, extract(ucd.item_data,'/*/*') item_data
183-
from ut_data_set_tmp ucd
184-
where ucd.data_set_guid = :self_guid
183+
from ut_compound_data_tmp ucd
184+
where ucd.data_id = :self_guid
185185
and ucd.item_no in (select i.item_no from diff_info i)
186186
) exp
187187
full outer join (
188188
select ucd.item_no, extract(ucd.item_data,'/*/*') item_data
189-
from ut_data_set_tmp ucd
190-
where ucd.data_set_guid = :other_guid
189+
from ut_compound_data_tmp ucd
190+
where ucd.data_id = :other_guid
191191
and ucd.item_no in (select i.item_no from diff_info i)
192192

193193
)act

source/expectations/data_values/ut_refcursor_helper.pks renamed to source/expectations/data_values/ut_compound_data_helper.pks

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace package ut_refcursor_helper authid definer is
1+
create or replace package ut_compound_data_helper authid definer is
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project

source/expectations/data_values/ut_data_set_tmp.sql renamed to source/expectations/data_values/ut_compound_data_tmp.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create global temporary table ut_data_set_tmp(
1+
create global temporary table ut_compound_data_tmp(
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -12,8 +12,8 @@ create global temporary table ut_data_set_tmp(
1212
See the License for the specific language governing permissions and
1313
limitations under the License.
1414
*/
15-
data_set_guid raw(16),
15+
data_id raw(16),
1616
item_no integer,
1717
item_data xmltype,
18-
constraint ut_data_set_tmp_pk primary key(data_set_guid, item_no)
18+
constraint ut_compound_data_tmp_pk primary key(data_id, item_no)
1919
) on commit preserve rows;
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
create or replace type body ut_compound_data_value as
2+
/*
3+
utPLSQL - Version 3
4+
Copyright 2016 - 2017 utPLSQL Project
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"):
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
overriding member function get_object_info return varchar2 is
20+
begin
21+
return self.data_type||' [ count = '||self.elements_count||' ]';
22+
end;
23+
24+
overriding member function is_null return boolean is
25+
begin
26+
return ut_utils.int_to_boolean(self.is_data_null);
27+
end;
28+
29+
overriding member function is_diffable return boolean is
30+
begin
31+
return true;
32+
end;
33+
34+
member function is_empty return boolean is
35+
begin
36+
return self.elements_count = 0;
37+
end;
38+
39+
overriding member function is_multi_line return boolean is
40+
begin
41+
return not self.is_null() and not self.is_empty();
42+
end;
43+
44+
overriding member function compare_implementation(a_other ut_data_value) return integer is
45+
begin
46+
return compare_implementation( a_other, null, null);
47+
end;
48+
49+
overriding member function to_string return varchar2 is
50+
l_results ut_utils.t_clob_tab;
51+
c_max_rows constant integer := 20;
52+
l_result clob;
53+
l_result_string varchar2(32767);
54+
begin
55+
if not self.is_null() then
56+
dbms_lob.createtemporary(l_result, true);
57+
ut_utils.append_to_clob(l_result,'Data:'||chr(10));
58+
--return first c_max_rows rows
59+
execute immediate '
60+
select xmlserialize( content ucd.item_data no indent)
61+
from '|| ut_utils.ut_owner ||'.ut_compound_data_tmp ucd
62+
where ucd.data_id = :data_id
63+
and ucd.item_no <= :max_rows'
64+
bulk collect into l_results using self.data_id, c_max_rows;
65+
66+
ut_utils.append_to_clob(l_result,l_results);
67+
68+
l_result_string := ut_utils.to_string(l_result,null);
69+
dbms_lob.freetemporary(l_result);
70+
end if;
71+
return l_result_string;
72+
end;
73+
74+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2 is
75+
l_result clob;
76+
l_result_string varchar2(32767);
77+
begin
78+
l_result := get_data_diff(a_other, a_exclude_xpath, a_include_xpath);
79+
l_result_string := ut_utils.to_string(l_result,null);
80+
dbms_lob.freetemporary(l_result);
81+
return l_result_string;
82+
end;
83+
84+
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return clob is
85+
c_max_rows constant integer := 20;
86+
l_result clob;
87+
l_results ut_utils.t_clob_tab := ut_utils.t_clob_tab();
88+
l_message varchar2(32767);
89+
l_ut_owner varchar2(250) := ut_utils.ut_owner;
90+
l_diff_row_count integer;
91+
l_actual ut_compound_data_value;
92+
l_diff_id raw(16);
93+
l_row_diffs ut_compound_data_helper.tt_row_diffs;
94+
begin
95+
if not a_other is of (ut_compound_data_value) then
96+
raise value_error;
97+
end if;
98+
l_actual := treat(a_other as ut_compound_data_value);
99+
100+
dbms_lob.createtemporary(l_result,true);
101+
102+
--diff rows and row elements
103+
l_diff_id := dbms_crypto.hash(self.data_id||l_actual.data_id,2);
104+
-- First tell how many rows are different
105+
execute immediate 'select count(*) from ' || l_ut_owner || '.ut_compound_data_diff_tmp where diff_id = :diff_id' into l_diff_row_count using l_diff_id;
106+
107+
if l_diff_row_count > 0 then
108+
l_row_diffs := ut_compound_data_helper.get_rows_diff(
109+
self.data_id, l_actual.data_id, l_diff_id, c_max_rows, a_exclude_xpath, a_include_xpath
110+
);
111+
l_message := chr(10)
112+
||'Rows: [ ' || l_diff_row_count ||' differences'
113+
|| case when l_diff_row_count > c_max_rows and l_row_diffs.count > 0 then ', showing first '||c_max_rows end
114+
||' ]' || chr(10)
115+
|| case when l_row_diffs.count = 0
116+
then ' All rows are different as the columns are not matching.' end;
117+
ut_utils.append_to_clob( l_result, l_message );
118+
for i in 1 .. l_row_diffs.count loop
119+
l_results.extend;
120+
l_results(l_results.last) := ' Row No. '||l_row_diffs(i).rn||' - '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row;
121+
end loop;
122+
ut_utils.append_to_clob(l_result,l_results);
123+
end if;
124+
return l_result;
125+
end;
126+
127+
member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer is
128+
l_other ut_compound_data_value;
129+
l_ut_owner varchar2(250) := ut_utils.ut_owner;
130+
l_column_filter varchar2(32767);
131+
l_diff_id raw(16);
132+
l_result integer;
133+
--the XML stylesheet is applied on XML representation of data to exclude column names from comparison
134+
--column names and data-types are compared separately
135+
l_xml_data_fmt constant xmltype := xmltype(
136+
q'[<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
137+
<xsl:strip-space elements="*" />
138+
<xsl:template match="/child::*">
139+
<xsl:for-each select="child::node()">
140+
<xsl:choose>
141+
<xsl:when test="*[*]"><xsl:copy-of select="node()"/></xsl:when>
142+
<xsl:when test="position()=last()"><xsl:value-of select="normalize-space(.)"/><xsl:text>&#xD;</xsl:text></xsl:when>
143+
<xsl:otherwise><xsl:value-of select="normalize-space(.)"/>,</xsl:otherwise>
144+
</xsl:choose>
145+
</xsl:for-each>
146+
</xsl:template>
147+
</xsl:stylesheet>]');
148+
begin
149+
if not a_other is of (ut_compound_data_value) then
150+
raise value_error;
151+
end if;
152+
153+
l_other := treat(a_other as ut_compound_data_value);
154+
155+
l_diff_id := dbms_crypto.hash(self.data_id||l_other.data_id,2);
156+
l_column_filter := ut_compound_data_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
157+
-- Find differences
158+
execute immediate 'insert into ' || l_ut_owner || '.ut_compound_data_diff_tmp ( diff_id, item_no )
159+
select :diff_id, nvl(exp.item_no, act.item_no)
160+
from (select '||l_column_filter||', ucd.item_no
161+
from ' || l_ut_owner || '.ut_compound_data_tmp ucd where ucd.data_id = :self_guid) exp
162+
full outer join
163+
(select '||l_column_filter||', ucd.item_no
164+
from ' || l_ut_owner || '.ut_compound_data_tmp ucd where ucd.data_id = :l_other_guid) act
165+
on exp.item_no = act.item_no '||
166+
'where nvl( dbms_lob.compare(' ||
167+
/*the xmltransform removes column names and leaves column data to be compared only*/
168+
' xmltransform(exp.item_data, :l_xml_data_fmt).getclobval()' ||
169+
', xmltransform(act.item_data, :l_xml_data_fmt).getclobval())' ||
170+
',1' ||
171+
') != 0'
172+
using in l_diff_id, a_exclude_xpath, a_include_xpath, self.data_id,
173+
a_exclude_xpath, a_include_xpath, l_other.data_id, l_xml_data_fmt, l_xml_data_fmt;
174+
175+
--result is OK only if both are same
176+
if sql%rowcount = 0 and self.elements_count = l_other.elements_count then
177+
l_result := 0;
178+
else
179+
l_result := 1;
180+
end if;
181+
return l_result;
182+
end;
183+
184+
end;
185+
/
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
create or replace type ut_compound_data_value under ut_data_value(
2+
/*
3+
utPLSQL - Version 3
4+
Copyright 2016 - 2017 utPLSQL Project
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"):
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
/**
19+
* Holds information about ref cursor to be processed by expectation
20+
*/
21+
22+
23+
/**
24+
* Determines if the cursor is null
25+
*/
26+
is_data_null integer,
27+
28+
/**
29+
* Holds the number of elements in the compound data value (cursor/collection)
30+
*/
31+
elements_count integer,
32+
33+
/**
34+
* Holds unique id for retrieving the data from ut_compound_data_tmp temp table
35+
*/
36+
data_id raw(16),
37+
38+
overriding member function get_object_info return varchar2,
39+
overriding member function is_null return boolean,
40+
overriding member function is_diffable return boolean,
41+
member function is_empty return boolean,
42+
overriding member function to_string return varchar2,
43+
overriding member function is_multi_line return boolean,
44+
overriding member function compare_implementation(a_other ut_data_value) return integer,
45+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2,
46+
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return clob,
47+
member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer
48+
) not final not instantiable
49+
/

0 commit comments

Comments
 (0)