Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b9ca972

Browse files
committed
Initial check in of unordered comparision
1 parent 24c962b commit b9ca972

13 files changed

Lines changed: 227 additions & 27 deletions

source/expectations/data_values/ut_compound_data_helper.pkb

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,10 @@ create or replace package body ut_compound_data_helper is
217217
from ut_compound_data_tmp ucd
218218
where ucd.data_id = :other_guid
219219
and ucd.item_no in (select i.item_no from diff_info i)
220-
221220
)act
222221
on exp.item_no = act.item_no
223222
where exp.item_no is null or act.item_no is null
224-
order by 1, 2
225-
]'
223+
order by 1, 2]'
226224
bulk collect into l_results
227225
using a_diff_id, a_max_rows,
228226
a_exclude_xpath, a_include_xpath, a_expected_dataset_guid,
@@ -231,6 +229,56 @@ create or replace package body ut_compound_data_helper is
231229
return l_results;
232230
end;
233231

232+
function get_rows_diff_unordered(
233+
a_expected_dataset_guid raw, a_actual_dataset_guid raw, a_diff_id raw,
234+
a_max_rows integer, a_exclude_xpath varchar2, a_include_xpath varchar2
235+
) return tt_row_diffs is
236+
l_column_filter varchar2(32767);
237+
l_results tt_row_diffs;
238+
begin
239+
l_column_filter := get_columns_filter(a_exclude_xpath,a_include_xpath);
240+
execute immediate q'[
241+
select
242+
coalesce(exp.duplicate_no, act.duplicate_no) duplicate_no,
243+
case when exp.row_hash is null then 'Actual:' else 'Expected:' end diffed_type,
244+
case when exp.row_hash is null then
245+
xmlserialize(content act.row_data no indent)
246+
else
247+
xmlserialize(content exp.row_data no indent)
248+
end diffed_row
249+
from (select ucd.*, row_number() over(partition by row_hash order by row_hash) duplicate_no
250+
from (select ucd.column_value row_data,
251+
dbms_crypto.hash( value(ucd).getclobval(),3) row_hash
252+
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
253+
from ut_compound_data_tmp ucd
254+
where ucd.data_id = :self_guid
255+
) r,
256+
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
257+
) ucd
258+
) exp
259+
full outer join
260+
(select ucd.*, row_number() over(partition by row_hash order by row_hash) duplicate_no
261+
from (select ucd.column_value row_data,
262+
dbms_crypto.hash( value(ucd).getclobval(),3/*HASH_SH1*/) row_hash
263+
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
264+
from ut_compound_data_tmp ucd
265+
where ucd.data_id = :other_guid
266+
) r,
267+
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
268+
) ucd
269+
) act
270+
on exp.row_hash = act.row_hash
271+
and exp.duplicate_no = act.duplicate_no
272+
where exp.row_hash is null or act.row_hash is null]'
273+
bulk collect into l_results
274+
using a_exclude_xpath, a_include_xpath, a_expected_dataset_guid,
275+
a_exclude_xpath, a_include_xpath, a_actual_dataset_guid;
276+
277+
--execute immediate 'create table test as select * from ut_compound_data_tmp';
278+
return l_results;
279+
280+
end;
281+
234282
function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is
235283
begin
236284
return dbms_crypto.hash(a_data, a_hash_type);

source/expectations/data_values/ut_compound_data_helper.pks

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ create or replace package ut_compound_data_helper authid definer is
5252
a_max_rows integer, a_exclude_xpath varchar2, a_include_xpath varchar2
5353
) return tt_row_diffs;
5454

55+
function get_rows_diff_unordered(
56+
a_expected_dataset_guid raw, a_actual_dataset_guid raw, a_diff_id raw,
57+
a_max_rows integer, a_exclude_xpath varchar2, a_include_xpath varchar2
58+
) return tt_row_diffs;
59+
5560
subtype t_hash is raw(128);
5661

5762
function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash;

source/expectations/data_values/ut_compound_data_value.tpb

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,61 @@ create or replace type body ut_compound_data_value as
7171
return l_result_string;
7272
end;
7373

74-
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2 is
74+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean := false ) return varchar2 is
7575
l_result clob;
7676
l_result_string varchar2(32767);
7777
begin
78-
l_result := get_data_diff(a_other, a_exclude_xpath, a_include_xpath);
78+
l_result := get_data_diff(a_other, a_exclude_xpath, a_include_xpath, a_unordered);
7979
l_result_string := ut_utils.to_string(l_result,null);
8080
dbms_lob.freetemporary(l_result);
8181
return l_result_string;
8282
end;
8383

84-
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return clob is
84+
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean ) 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 ut_compound_data_helper.t_hash;
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 := ut_compound_data_helper.get_hash(self.data_id||l_actual.data_id);
104+
-- First tell how many rows are different
105+
execute immediate 'select unordered_cnt 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_unordered(
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) :=
121+
' Row Appeared '||l_row_diffs(i).rn||' - '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row;
122+
end loop;
123+
ut_utils.append_to_clob(l_result,l_results);
124+
end if;
125+
return l_result;
126+
end;
127+
128+
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return clob is
85129
c_max_rows constant integer := 20;
86130
l_result clob;
87131
l_results ut_utils.t_clob_tab := ut_utils.t_clob_tab();
@@ -105,9 +149,9 @@ create or replace type body ut_compound_data_value as
105149
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;
106150

107151
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-
);
152+
l_row_diffs := ut_compound_data_helper.get_rows_diff(
153+
self.data_id, l_actual.data_id, l_diff_id, c_max_rows, a_exclude_xpath, a_include_xpath
154+
);
111155
l_message := chr(10)
112156
||'Rows: [ ' || l_diff_row_count ||' differences'
113157
|| case when l_diff_row_count > c_max_rows and l_row_diffs.count > 0 then ', showing first '||c_max_rows end
@@ -171,7 +215,6 @@ create or replace type body ut_compound_data_value as
171215
') != 0'
172216
using in l_diff_id, a_exclude_xpath, a_include_xpath, self.data_id,
173217
a_exclude_xpath, a_include_xpath, l_other.data_id, l_xml_data_fmt, l_xml_data_fmt;
174-
175218
--result is OK only if both are same
176219
if sql%rowcount = 0 and self.elements_count = l_other.elements_count then
177220
l_result := 0;
@@ -181,5 +224,39 @@ create or replace type body ut_compound_data_value as
181224
return l_result;
182225
end;
183226

227+
member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean ) return integer is
228+
l_other ut_compound_data_value;
229+
l_ut_owner varchar2(250) := ut_utils.ut_owner;
230+
l_column_filter varchar2(32767);
231+
l_diff_id ut_compound_data_helper.t_hash;
232+
l_result integer;
233+
l_row_diffs ut_compound_data_helper.tt_row_diffs;
234+
c_max_rows constant integer := 20;
235+
begin
236+
if not a_other is of (ut_compound_data_value) then
237+
raise value_error;
238+
end if;
239+
240+
l_other := treat(a_other as ut_compound_data_value);
241+
242+
l_diff_id := ut_compound_data_helper.get_hash(self.data_id||l_other.data_id);
243+
l_column_filter := ut_compound_data_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
244+
245+
-- Find differences
246+
l_row_diffs := ut_compound_data_helper.get_rows_diff_unordered(
247+
self.data_id, l_other.data_id, l_diff_id, c_max_rows, a_exclude_xpath, a_include_xpath
248+
);
249+
250+
execute immediate 'insert into ' || l_ut_owner || '.ut_compound_data_diff_tmp ( diff_id, unordered_cnt )
251+
values (:diff_id, nvl(:unordered_cnt,0))' using l_diff_id, l_row_diffs.count;
252+
--result is OK only if both are same
253+
if l_row_diffs.count = 0 and self.elements_count = l_other.elements_count then
254+
l_result := 0;
255+
else
256+
l_result := 1;
257+
end if;
258+
return l_result;
259+
end;
260+
184261
end;
185262
/

source/expectations/data_values/ut_compound_data_value.tps

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace type ut_compound_data_value under ut_data_value(
1+
create or replace type ut_compound_data_value force under ut_data_value(
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -42,8 +42,10 @@ create or replace type ut_compound_data_value under ut_data_value(
4242
overriding member function to_string return varchar2,
4343
overriding member function is_multi_line return boolean,
4444
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,
45+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean := false ) return varchar2,
46+
member function get_data_diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean ) return clob,
4647
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+
member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer,
49+
member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean ) return integer
4850
) not final not instantiable
4951
/

source/expectations/data_values/ut_data_value.tpb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ create or replace type body ut_data_value as
2424
begin
2525
return false;
2626
end;
27-
28-
member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2 is
27+
28+
member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean :=false ) return varchar2 is
2929
begin
3030
return null;
3131
end;

source/expectations/data_values/ut_data_value.tps

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace type ut_data_value authid current_user as object (
1+
create or replace type ut_data_value force authid current_user as object (
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -24,7 +24,7 @@ create or replace type ut_data_value authid current_user as object (
2424
final member function to_string_report(a_add_new_line_for_multi_line boolean := false, a_with_object_info boolean := true) return varchar2,
2525
order member function compare( a_other ut_data_value ) return integer,
2626
member function is_diffable return boolean,
27-
member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2,
27+
member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean := false ) return varchar2,
2828
not instantiable member function compare_implementation( a_other ut_data_value ) return integer
2929
) not final not instantiable
3030
/

source/expectations/data_values/ut_data_value_anydata.tps

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace type ut_data_value_anydata under ut_compound_data_value(
1+
create or replace type ut_data_value_anydata force under ut_compound_data_value(
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project

source/expectations/data_values/ut_data_value_refcursor.tpb

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ create or replace type body ut_data_value_refcursor as
108108
return l_result_string;
109109
end;
110110

111-
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2 is
111+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean := false ) return varchar2 is
112112
l_result clob;
113113
l_results ut_utils.t_clob_tab := ut_utils.t_clob_tab();
114114
l_result_string varchar2(32767);
@@ -176,8 +176,12 @@ create or replace type body ut_data_value_refcursor as
176176
end if;
177177

178178
--diff rows and row elements
179-
ut_utils.append_to_clob(l_result, self.get_data_diff(a_other, l_exclude_xpath, a_include_xpath));
180-
179+
if a_unordered then
180+
ut_utils.append_to_clob(l_result, self.get_data_diff(a_other, l_exclude_xpath, a_include_xpath, a_unordered));
181+
else
182+
ut_utils.append_to_clob(l_result, self.get_data_diff(a_other, l_exclude_xpath, a_include_xpath));
183+
end if;
184+
181185
l_result_string := ut_utils.to_string(l_result,null);
182186
dbms_lob.freetemporary(l_result);
183187
return l_result_string;
@@ -203,5 +207,26 @@ create or replace type body ut_data_value_refcursor as
203207
return l_result;
204208
end;
205209

210+
overriding member function compare_implementation (a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean) return integer is
211+
l_result integer := 0;
212+
l_other ut_data_value_refcursor;
213+
begin
214+
if not a_other is of (ut_data_value_refcursor) then
215+
raise value_error;
216+
end if;
217+
218+
l_other := treat(a_other as ut_data_value_refcursor);
219+
220+
--if column names/types are not equal - build a diff of column names and types
221+
if ut_compound_data_helper.columns_hash( self, a_exclude_xpath, a_include_xpath )
222+
!= ut_compound_data_helper.columns_hash( l_other, a_exclude_xpath, a_include_xpath )
223+
then
224+
l_result := 1;
225+
end if;
226+
l_result := l_result + (self as ut_compound_data_value).compare_implementation(a_other, a_exclude_xpath, a_include_xpath, a_unordered);
227+
return l_result;
228+
end;
229+
230+
206231
end;
207232
/

source/expectations/data_values/ut_data_value_refcursor.tps

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ create or replace type ut_data_value_refcursor under ut_compound_data_value(
3333
constructor function ut_data_value_refcursor(self in out nocopy ut_data_value_refcursor, a_value sys_refcursor) return self as result,
3434
member procedure init(self in out nocopy ut_data_value_refcursor, a_value sys_refcursor),
3535
overriding member function to_string return varchar2,
36-
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2 ) return varchar2,
37-
overriding member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer
36+
overriding member function diff( a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean := false ) return varchar2,
37+
overriding member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer,
38+
overriding member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2, a_unordered boolean) return integer
39+
3840
)
3941
/

source/expectations/matchers/ut_equal.tpb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ create or replace type body ut_equal as
182182
return l_result;
183183
end;
184184

185+
member function unordered return ut_equal is
186+
l_result ut_equal := self;
187+
begin
188+
l_result.is_unordered := 'true';
189+
return l_result;
190+
end;
191+
185192
member function get_include_xpath return varchar2 is
186193
begin
187194
return ut_utils.to_xpath( coalesce(include_list, ut_varchar2_list()) );
@@ -192,14 +199,24 @@ create or replace type body ut_equal as
192199
return ut_utils.to_xpath( coalesce(exclude_list, ut_varchar2_list()) );
193200
end;
194201

202+
member function get_unordered return boolean is
203+
begin
204+
return case when is_unordered = 'true' then true else false end;
205+
end;
206+
207+
195208
overriding member function run_matcher(self in out nocopy ut_equal, a_actual ut_data_value) return boolean is
196209
l_result boolean;
197210
begin
198211
if self.expected.data_type = a_actual.data_type then
199212
if self.expected is of (ut_data_value_anydata) then
200213
l_result := 0 = treat(self.expected as ut_data_value_anydata).compare_implementation(a_actual, get_exclude_xpath(), get_include_xpath());
201214
elsif self.expected is of (ut_data_value_refcursor) then
202-
l_result := 0 = treat(self.expected as ut_data_value_refcursor).compare_implementation(a_actual, get_exclude_xpath(), get_include_xpath());
215+
if get_unordered then
216+
l_result := 0 = treat(self.expected as ut_data_value_refcursor).compare_implementation(a_actual, get_exclude_xpath(), get_include_xpath(),get_unordered());
217+
else
218+
l_result := 0 = treat(self.expected as ut_data_value_refcursor).compare_implementation(a_actual, get_exclude_xpath(), get_include_xpath());
219+
end if;
203220
else
204221
l_result := equal_with_nulls((self.expected = a_actual), a_actual);
205222
end if;
@@ -216,7 +233,7 @@ create or replace type body ut_equal as
216233
if self.expected.data_type = a_actual.data_type and self.expected.is_diffable then
217234
l_result :=
218235
'Actual: '||a_actual.get_object_info()||' '||self.description()||': '||self.expected.get_object_info()
219-
|| chr(10) || 'Diff:' || expected.diff(a_actual, get_exclude_xpath(), get_include_xpath());
236+
|| chr(10) || 'Diff:' || expected.diff(a_actual, get_exclude_xpath(), get_include_xpath(), get_unordered());
220237
else
221238
l_result := (self as ut_matcher).failure_message(a_actual) || ': '|| self.expected.to_string_report();
222239
end if;

0 commit comments

Comments
 (0)