@@ -28,21 +28,22 @@ create or replace type body ut_data_value_refcursor as
2828 end;
2929
3030 member procedure init(self in out nocopy ut_data_value_refcursor, a_value sys_refcursor) is
31+ c_bulk_rows constant integer := 1000;
32+ l_cursor sys_refcursor := a_value;
3133 l_ctx number;
3234 l_xml xmltype;
33- c_bulk_rows constant integer := 1000;
3435 l_current_date_format varchar2(4000);
35- l_ut_owner varchar2(250) := ut_utils.ut_owner;
36- cursor_not_open exception;
36+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
37+ cursor_not_open exception;
3738 begin
3839 self.is_cursor_null := ut_utils.boolean_to_int(a_value is null);
3940 self.self_type := $$plsql_unit;
4041 self.data_set_guid := sys_guid();
4142 self.data_type := 'refcursor';
42- self.columns_info := ut_refcursor_descriptor.get_columns_info(a_value);
43- if a_value is not null then
44- if a_value%isopen then
45- self.row_count := 0;
43+ if l_cursor is not null then
44+ if l_cursor%isopen then
45+ self.columns_info := ut_refcursor_helper.get_columns_info(l_cursor);
46+ self.row_count := 0;
4647 -- We use DBMS_XMLGEN in order to:
4748 -- 1) be able to process data in bulks (set of rows)
4849 -- 2) be able to influence the ROWSET/ROW tags
@@ -56,15 +57,17 @@ create or replace type body ut_data_value_refcursor as
5657 -- The restartQuery fails however if PLSQL variables of TIMESTAMP/INTERVAL or CLOB/BLOB are used.
5758
5859 ut_expectation_processor.set_xml_nls_params();
59- l_ctx := dbms_xmlgen.newContext(a_value );
60+ l_ctx := dbms_xmlgen.newContext(l_cursor );
6061 dbms_xmlgen.setNullHandling(l_ctx, dbms_xmlgen.empty_tag);
6162 dbms_xmlgen.setMaxRows(l_ctx, c_bulk_rows);
6263
6364 loop
6465 l_xml := dbms_xmlgen.getxmltype(l_ctx);
6566
66- execute immediate 'insert into ' || l_ut_owner || '.ut_data_set_tmp(data_set_guid, item_no, item_data)
67- select :self_guid, :self_row_count + rownum, value(a) from table( xmlsequence( extract(:l_xml,''ROWSET/*'') ) ) a'
67+ execute immediate
68+ 'insert into ' || l_ut_owner || '.ut_data_set_tmp(data_set_guid, item_no, item_data) ' ||
69+ 'select :self_guid, :self_row_count + rownum, value(a) ' ||
70+ ' from table( xmlsequence( extract(:l_xml,''ROWSET/*'') ) ) a'
6871 using in self.data_set_guid, self.row_count, l_xml;
6972
7073 exit when sql%rowcount = 0;
@@ -73,12 +76,12 @@ create or replace type body ut_data_value_refcursor as
7376 end loop;
7477
7578 ut_expectation_processor.reset_nls_params();
76- if a_value %isopen then
77- close a_value ;
79+ if l_cursor %isopen then
80+ close l_cursor ;
7881 end if;
7982 dbms_xmlgen.closeContext(l_ctx);
8083
81- elsif not a_value %isopen then
84+ elsif not l_cursor %isopen then
8285 raise cursor_not_open;
8386 end if;
8487 end if;
@@ -87,8 +90,8 @@ create or replace type body ut_data_value_refcursor as
8790 raise_application_error(-20155, 'Cursor is not open');
8891 when others then
8992 ut_expectation_processor.reset_nls_params();
90- if a_value %isopen then
91- close a_value ;
93+ if l_cursor %isopen then
94+ close l_cursor ;
9295 end if;
9396 dbms_xmlgen.closeContext(l_ctx);
9497 raise;
@@ -105,19 +108,25 @@ create or replace type body ut_data_value_refcursor as
105108 l_result clob;
106109 l_result_string varchar2(32767);
107110 begin
108- dbms_lob.createtemporary(l_result,true);
109- --return first 10 rows
110- execute immediate '
111- select xmlserialize( content ucd.item_data no indent)
112- from '|| ut_utils.ut_owner ||'.ut_data_set_tmp ucd
113- where ucd.data_set_guid = :data_set_guid
114- and ucd.item_no <= :max_rows'
115- bulk collect into l_results using self.data_set_guid, c_max_rows;
116-
117- ut_utils.append_to_clob(l_result,l_results);
118-
119- l_result_string := ut_utils.to_string(l_result,null);
120- dbms_lob.freetemporary(l_result);
111+ if not self.is_null() then
112+ dbms_lob.createtemporary(l_result,true);
113+ ut_utils.append_to_clob(l_result,'Data-types:'||chr(10));
114+ ut_utils.append_to_clob(l_result,self.columns_info.getclobval());
115+
116+ ut_utils.append_to_clob(l_result,chr(10)||'Data:'||chr(10));
117+ --return first c_max_rows rows
118+ execute immediate '
119+ select xmlserialize( content ucd.item_data no indent)
120+ from '|| ut_utils.ut_owner ||'.ut_data_set_tmp ucd
121+ where ucd.data_set_guid = :data_set_guid
122+ and ucd.item_no <= :max_rows'
123+ bulk collect into l_results using self.data_set_guid, c_max_rows;
124+
125+ ut_utils.append_to_clob(l_result,l_results);
126+
127+ l_result_string := ut_utils.to_string(l_result,null);
128+ dbms_lob.freetemporary(l_result);
129+ end if;
121130 return l_result_string;
122131 end;
123132
@@ -173,30 +182,41 @@ create or replace type body ut_data_value_refcursor as
173182 end;
174183
175184 member function compare_implementation(a_other ut_data_value, a_exclude_xpath varchar2, a_include_xpath varchar2) return integer is
176- l_result integer;
177- l_other ut_data_value_refcursor;
178- l_ut_owner varchar2(250) := ut_utils.ut_owner;
179- l_column_filter varchar2(32767);
180- l_diff_id raw(16);
185+ l_result integer := 0;
186+ l_other ut_data_value_refcursor;
187+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
188+ l_column_filter varchar2(32767);
189+ l_diff_id raw(16);
190+ function columns_hash(
191+ a_data_value_cursor ut_data_value_refcursor, a_exclude_xpath varchar2, a_include_xpath varchar2
192+ ) return raw is
193+ l_cols_hash raw(32);
194+ begin
195+ if not a_data_value_cursor.is_null then
196+ execute immediate
197+ q'[select dbms_crypto.hash(replace(x.item_data.getclobval(),'>CHAR<','>VARCHAR2<'),3) ]' ||
198+ ' from ( select '||ut_refcursor_helper.get_columns_filter(a_exclude_xpath, a_include_xpath)||
199+ ' from (select :columns_info as item_data from dual ) ucd' ||
200+ ' ) x'
201+ into l_cols_hash using a_exclude_xpath, a_include_xpath, a_data_value_cursor.columns_info;
202+ end if;
203+ return l_cols_hash;
204+ end;
181205 begin
182- -- this SQL statement is constructed in a way that we always get the same number and ordering of substitution variables
183- -- That is, we always get: l_exclude_xpath, l_include_xpath
184- -- regardless if the variables are NULL (not to be used) or NOT NULL and will be used for filtering
185- if a_exclude_xpath is null and a_include_xpath is null then
186- l_column_filter := ':l_exclude_xpath as l_exclude_xpath, :l_include_xpath as l_include_xpath, ucd.item_data as item_data';
187- elsif a_exclude_xpath is not null and a_include_xpath is null then
188- l_column_filter := 'deletexml( ucd.item_data, :l_exclude_xpath ) as item_data, :l_include_xpath as l_include_xpath';
189- elsif a_exclude_xpath is null and a_include_xpath is not null then
190- l_column_filter := ':l_exclude_xpath as l_exclude_xpath, extract( ucd.item_data, :l_include_xpath ) as item_data';
191- elsif a_exclude_xpath is not null and a_include_xpath is not null then
192- l_column_filter := 'extract( deletexml( ucd.item_data, :l_exclude_xpath ), :l_include_xpath ) as item_data';
193- end if;
194206 if not a_other is of (ut_data_value_refcursor) then
195207 raise value_error;
196208 end if;
197209
198210 l_other := treat(a_other as ut_data_value_refcursor);
211+
212+ --if column names/types are not equal - build a diff of column names and types
213+ if columns_hash( self, a_exclude_xpath, a_include_xpath )
214+ != columns_hash( l_other, a_exclude_xpath, a_include_xpath )
215+ then
216+ l_result := 1;
217+ end if;
199218 l_diff_id := dbms_crypto.hash(self.data_set_guid||l_other.data_set_guid,2);
219+ l_column_filter := ut_refcursor_helper.get_columns_filter(a_exclude_xpath, a_include_xpath);
200220 -- Find differences
201221 execute immediate 'insert into ' || l_ut_owner || '.ut_data_set_diff_tmp ( diff_id, item_no )
202222 select :diff_id, nvl(exp.item_no, act.item_no)
@@ -210,7 +230,7 @@ create or replace type body ut_data_value_refcursor as
210230 using in l_diff_id, a_exclude_xpath, a_include_xpath, self.data_set_guid, a_exclude_xpath, a_include_xpath, l_other.data_set_guid;
211231
212232 --result is OK only if both are same
213- if sql%rowcount = 0 and self.row_count = l_other.row_count then
233+ if sql%rowcount = 0 and self.row_count = l_other.row_count and l_result = 0 then
214234 l_result := 0;
215235 else
216236 l_result := 1;
0 commit comments