@@ -49,7 +49,7 @@ create or replace type body ut_data_value_refcursor as
4949 begin
5050 self.is_cursor_null := ut_utils.boolean_to_int(a_value is null);
5151 self.self_type := $$plsql_unit;
52- self.data_value := sys_guid();
52+ self.data_set_guid := sys_guid();
5353 self.data_type := 'refcursor';
5454
5555 if a_value is not null then
@@ -75,9 +75,9 @@ create or replace type body ut_data_value_refcursor as
7575 loop
7676 l_xml := dbms_xmlgen.getxmltype(l_ctx);
7777
78- execute immediate 'insert into ' || l_ut_owner || '.ut_cursor_data(cursor_data_guid, row_no, row_data )
78+ execute immediate 'insert into ' || l_ut_owner || '.ut_data_set_tmp(data_set_guid, item_no, item_data )
7979 select :self_guid, :self_row_count + rownum, value(a) from table( xmlsequence( extract(:l_xml,''ROWSET/*'') ) ) a'
80- using in self.data_value , self.row_count, l_xml;
80+ using in self.data_set_guid , self.row_count, l_xml;
8181
8282 exit when sql%rowcount = 0;
8383
@@ -112,40 +112,67 @@ create or replace type body ut_data_value_refcursor as
112112 end;
113113
114114 overriding member function to_string return varchar2 is
115- type t_clob_tab is table of clob;
116- l_results t_clob_tab;
117- c_max_rows constant integer := 50;
118- c_pad_depth constant integer := 5;
115+ l_results ut_utils.t_clob_tab;
116+ c_max_rows constant integer := 10;
119117 l_result clob;
120- l_result_xml xmltype;
121118 l_result_string varchar2(32767);
122- l_ut_owner varchar2(250) := ut_utils.ut_owner;
119+ begin
120+ dbms_lob.createtemporary(l_result,true);
121+ --return first 10 rows
122+ execute immediate '
123+ select xmlserialize( content ucd.item_data no indent)
124+ from '|| ut_utils.ut_owner ||'.ut_data_set_tmp ucd
125+ where ucd.data_set_guid = :data_set_guid
126+ and ucd.item_no <= :max_rows'
127+ bulk collect into l_results using self.data_set_guid, c_max_rows;
128+
129+ ut_utils.append_to_clob(l_result,'row count: '||row_count);
130+ ut_utils.append_to_clob(l_result,l_results);
131+
132+ l_result_string := ut_utils.to_string(l_result,null);
133+ dbms_lob.freetemporary(l_result);
134+ return self.format_multi_line( l_result_string );
135+ end;
136+
137+ overriding member function is_diffable return boolean is
138+ begin
139+ return true;
140+ end;
141+
142+ overriding member function diff( a_other ut_data_value ) return varchar2 is
143+ c_max_rows constant integer := 50;
144+ c_pad_depth constant integer := 5;
145+ l_results ut_utils.t_clob_tab;
146+ l_result clob;
147+ l_result_string varchar2(32767);
148+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
123149 l_diff_row_count integer;
150+ l_other ut_data_value_refcursor;
151+ l_diff_id raw(16);
124152 begin
153+ if not a_other is of (ut_data_value_refcursor) then
154+ raise value_error;
155+ end if;
156+ l_other := treat(a_other as ut_data_value_refcursor);
157+ l_diff_id := dbms_crypto.hash(self.data_set_guid||l_other.data_set_guid,2);
125158 dbms_lob.createtemporary(l_result,true);
126159 -- First tell how many rows are different
127- execute immediate 'select count(*) from ' || l_ut_owner || '.ut_cursor_data_diff' into l_diff_row_count;
128-
129- ut_utils.append_to_clob(l_result,'(rows: ' || to_char(self.row_count)|| ', mismatched: ' || to_char(l_diff_row_count) ||')'|| chr(10));
160+ execute immediate 'select count(*) from ' || l_ut_owner || '.ut_data_set_diff_tmp where diff_id = :diff_id' into l_diff_row_count using l_diff_id;
130161
131162 --return rows which were previously marked as different
132- execute immediate q'[select 'row_no: '||rpad( ucd.row_no , :c_pad_depth )||' '||xmlserialize( content ucd.row_data no indent)
133- from ]' || l_ut_owner || '.ut_cursor_data ucd
134- where ucd.cursor_data_guid = :self_guid
135- and ucd.row_no in (select row_no from ' || l_ut_owner || '.ut_cursor_data_diff ucdc)
163+ execute immediate q'[select 'row_no: '||rpad( ucd.item_no , :c_pad_depth )||' '||xmlserialize( content ucd.item_data no indent)
164+ from ]' || l_ut_owner || '.ut_data_set_tmp ucd
165+ where ucd.data_set_guid = :self_guid
166+ and ucd.item_no in (select item_no from ' || l_ut_owner || '.ut_data_set_diff_tmp ucdc where diff_id = :diff_id )
136167 and rownum <= :max_rows'
137- bulk collect into l_results using c_pad_depth, self.data_value , c_max_rows;
168+ bulk collect into l_results using c_pad_depth, self.data_set_guid, l_diff_id , c_max_rows;
138169
139- for i in 1 .. l_results.count loop
140- dbms_lob.append(l_result,l_results(i));
141- if i < l_results.count then
142- ut_utils.append_to_clob(l_result,chr(10));
143- end if;
144- end loop;
170+ ut_utils.append_to_clob(l_result,'(count: ' || to_char(l_diff_row_count) ||')');
171+ ut_utils.append_to_clob(l_result,l_results);
145172
146173 l_result_string := ut_utils.to_string(l_result,null);
147174 dbms_lob.freetemporary(l_result);
148- return self.format_multi_line( l_result_string ) ;
175+ return l_result_string;
149176 end;
150177
151178 member function is_empty return boolean is
@@ -160,44 +187,46 @@ create or replace type body ut_data_value_refcursor as
160187 l_include_xpath varchar2(32767);
161188 l_ut_owner varchar2(250) := ut_utils.ut_owner;
162189 l_column_filter varchar2(32767);
190+ l_diff_id raw(16);
163191 begin
164192 l_exclude_xpath := coalesce(self.exclude_xpath, l_other.exclude_xpath);
165193 l_include_xpath := coalesce(self.include_xpath, l_other.include_xpath);
166194 -- this SQL statement is constructed in a way that we always get the same number and ordering of substitution variables
167195 -- That is, we always get: l_exclude_xpath, l_include_xpath
168196 -- regardless if the variables are NULL (not to be used) or NOT NULL and will be used for filtering
169197 if l_exclude_xpath is null and l_include_xpath is null then
170- l_column_filter := ':l_exclude_xpath as l_exclude_xpath, :l_include_xpath as l_include_xpath, ucd.row_data as row_data ';
198+ l_column_filter := ':l_exclude_xpath as l_exclude_xpath, :l_include_xpath as l_include_xpath, ucd.item_data as item_data ';
171199 elsif l_exclude_xpath is not null and l_include_xpath is null then
172- l_column_filter := 'deletexml( ucd.row_data , :l_exclude_xpath ) as row_data , :l_include_xpath as l_include_xpath';
200+ l_column_filter := 'deletexml( ucd.item_data , :l_exclude_xpath ) as item_data , :l_include_xpath as l_include_xpath';
173201 elsif l_exclude_xpath is null and l_include_xpath is not null then
174- l_column_filter := ':l_exclude_xpath as l_exclude_xpath, extract( ucd.row_data , :l_include_xpath ) as row_data ';
202+ l_column_filter := ':l_exclude_xpath as l_exclude_xpath, extract( ucd.item_data , :l_include_xpath ) as item_data ';
175203 elsif l_exclude_xpath is not null and l_include_xpath is not null then
176- l_column_filter := 'extract( deletexml( ucd.row_data , :l_exclude_xpath ), :l_include_xpath ) as row_data ';
204+ l_column_filter := 'extract( deletexml( ucd.item_data , :l_exclude_xpath ), :l_include_xpath ) as item_data ';
177205 end if;
178- if a_other is of (ut_data_value_refcursor) then
179- l_other := treat(a_other as ut_data_value_refcursor);
180- -- Find differences
181- execute immediate 'insert into ' || l_ut_owner || '.ut_cursor_data_diff ( row_no )
182- select nvl(exp.row_no, act.row_no)
183- from (select '||l_column_filter||', ucd.row_no
184- from ' || l_ut_owner || '.ut_cursor_data ucd where ucd.cursor_data_guid = :self_guid) exp
185- full outer join
186- (select '||l_column_filter||', ucd.row_no
187- from ' || l_ut_owner || '.ut_cursor_data ucd where ucd.cursor_data_guid = :l_other_guid) act
188- on exp.row_no = act.row_no
189- where nvl(dbms_lob.compare(xmlserialize( content exp.row_data no indent), xmlserialize( content act.row_data no indent)),1) != 0'
190- using in l_exclude_xpath, l_include_xpath, self.data_value, l_exclude_xpath, l_include_xpath, l_other.data_value;
191-
192- --result is OK only if both are same
193- if sql%rowcount = 0 and self.row_count = l_other.row_count then
194- l_result := 0;
195- else
196- l_result := 1;
197- end if;
198- else
206+ if not a_other is of (ut_data_value_refcursor) then
199207 raise value_error;
200208 end if;
209+
210+ l_other := treat(a_other as ut_data_value_refcursor);
211+ l_diff_id := dbms_crypto.hash(self.data_set_guid||l_other.data_set_guid,2);
212+ -- Find differences
213+ execute immediate 'insert into ' || l_ut_owner || '.ut_data_set_diff_tmp ( diff_id, item_no )
214+ select :diff_id, nvl(exp.item_no, act.item_no)
215+ from (select '||l_column_filter||', ucd.item_no
216+ from ' || l_ut_owner || '.ut_data_set_tmp ucd where ucd.data_set_guid = :self_guid) exp
217+ full outer join
218+ (select '||l_column_filter||', ucd.item_no
219+ from ' || l_ut_owner || '.ut_data_set_tmp ucd where ucd.data_set_guid = :l_other_guid) act
220+ on exp.item_no = act.item_no
221+ where nvl(dbms_lob.compare(xmlserialize( content exp.item_data no indent), xmlserialize( content act.item_data no indent)),1) != 0'
222+ using in l_diff_id, l_exclude_xpath, l_include_xpath, self.data_set_guid, l_exclude_xpath, l_include_xpath, l_other.data_set_guid;
223+
224+ --result is OK only if both are same
225+ if sql%rowcount = 0 and self.row_count = l_other.row_count then
226+ l_result := 0;
227+ else
228+ l_result := 1;
229+ end if;
201230 return l_result;
202231 end;
203232
0 commit comments