@@ -21,8 +21,72 @@ create or replace type body ut_data_value_anydata as
2121 return self.data_type || case when self.compound_type = 'collection' then ' [ count = '||self.elements_count||' ]' else null end;
2222 end;
2323
24- member procedure get_cursor_from_anydata(self in out nocopy ut_data_value_anydata, a_value in anydata,
25- a_refcursor out nocopy sys_refcursor) is
24+ member function get_extract_path(a_data_value anydata) return varchar2 is
25+ l_path varchar2(10);
26+ begin
27+ if self.compound_type = 'object' then
28+ l_path := '/*/*';
29+ else
30+ case when ut_metadata.has_collection_members(a_data_value) then
31+ l_path := '/*/*';
32+ else
33+ l_path := '/*';
34+ end case;
35+ end if;
36+ return l_path;
37+ end;
38+
39+ overriding member procedure extract_cursor(self in out nocopy ut_data_value_anydata, a_value sys_refcursor)
40+ is
41+ c_bulk_rows constant integer := 10000;
42+ l_cursor sys_refcursor := a_value;
43+ l_ctx number;
44+ l_xml xmltype;
45+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
46+ l_set_id integer := 0;
47+ l_elements_count number := 0;
48+ begin
49+ -- We use DBMS_XMLGEN in order to:
50+ -- 1) be able to process data in bulks (set of rows)
51+ -- 2) be able to influence the ROWSET/ROW tags
52+ -- 3) be able to influence the way NULL values are handled (empty TAG)
53+ -- 4) be able to influence the way TIMESTAMP is formatted.
54+ -- Due to Oracle feature/bug, it is not possible to change the DATE formatting of cursor data
55+ -- AFTER the cursor was opened.
56+ -- The only solution for this is to change NLS settings before opening the cursor.
57+ --
58+ -- This would work fine if we could use DBMS_XMLGEN.restartQuery.
59+ -- The restartQuery fails however if PLSQL variables of TIMESTAMP/INTERVAL or CLOB/BLOB are used.
60+ ut_expectation_processor.set_xml_nls_params();
61+ l_ctx := dbms_xmlgen.newContext(l_cursor);
62+ dbms_xmlgen.setNullHandling(l_ctx, dbms_xmlgen.empty_tag);
63+ dbms_xmlgen.setMaxRows(l_ctx, c_bulk_rows);
64+ loop
65+ l_xml := dbms_xmlgen.getxmltype(l_ctx);
66+ exit when dbms_xmlgen.getNumRowsProcessed(l_ctx) = 0;
67+ l_elements_count := l_elements_count + dbms_xmlgen.getNumRowsProcessed(l_ctx);
68+ execute immediate
69+ 'insert into ' || l_ut_owner || '.ut_compound_data_tmp(data_id, item_no, item_data) ' ||
70+ 'values (:self_guid, :self_row_count, :l_xml)'
71+ using in self.data_id, l_set_id, l_xml;
72+ l_set_id := l_set_id + c_bulk_rows;
73+ end loop;
74+ ut_expectation_processor.reset_nls_params();
75+ dbms_xmlgen.closeContext(l_ctx);
76+ self.elements_count := l_elements_count;
77+ exception
78+ when others then
79+ ut_expectation_processor.reset_nls_params();
80+ dbms_xmlgen.closeContext(l_ctx);
81+ raise;
82+ end;
83+
84+ member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata) is
85+ l_refcursor sys_refcursor;
86+ l_ctx number;
87+ l_ut_owner varchar2(250) := ut_utils.ut_owner;
88+ cursor_not_open exception;
89+ l_cursor_number number;
2690 l_anydata_sql varchar2(4000);
2791 l_cursor_sql varchar2(2000);
2892
@@ -45,14 +109,25 @@ create or replace type body ut_data_value_anydata as
45109 return resolve_name(a_datatype);
46110 end;
47111
48- begin
49- l_anydata_sql := '
112+ begin
113+ self.data_type := ut_metadata.get_anydata_typename(a_value);
114+ self.compound_type := get_instance(a_value);
115+ self.is_data_null := ut_metadata.is_anytype_null(a_value,self.compound_type);
116+ self.data_id := sys_guid();
117+ self.self_type := $$plsql_unit;
118+ self.cursor_details := ut_cursor_details();
119+
120+ ut_compound_data_helper.cleanup_diff;
121+
122+ if not self.is_null() then
123+ self.extract_path := get_extract_path(a_value);
124+ --get_cursor_from_anydata(a_value,l_refcursor);
125+ l_anydata_sql := '
50126 declare
51127 l_data '||self.data_type||';
52128 l_value anydata := :a_value;
53129 l_status integer;
54130 l_tmp_refcursor sys_refcursor;
55- l_refcursor sys_refcursor;
56131 begin
57132 l_status := l_value.get'||self.compound_type||'(l_data); '||
58133 case when self.compound_type = 'collection' then
@@ -61,46 +136,10 @@ create or replace type body ut_data_value_anydata as
61136 q'[ open :l_tmp_refcursor for select l_data as "]'||get_object_name(self.data_type)||q'[" from dual;]'
62137 end ||
63138 'end;';
64- execute immediate l_anydata_sql using in a_value, in out a_refcursor;
65- end;
66-
67- member function get_extract_path(a_data_value anydata) return varchar2 is
68- l_path varchar2(10);
69- begin
70- if self.compound_type = 'object' then
71- l_path := '/*/*';
72- else
73- case when ut_metadata.has_collection_members(a_data_value) then
74- l_path := '/*/*';
75- else
76- l_path := '/*';
77- end case;
78- end if;
79- return l_path;
80- end;
81-
82- member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata) is
83- l_refcursor sys_refcursor;
84- l_ctx number;
85- l_ut_owner varchar2(250) := ut_utils.ut_owner;
86- cursor_not_open exception;
87- l_cursor_number number;
88-
89- begin
90- self.data_type := ut_metadata.get_anydata_typename(a_value);
91- self.compound_type := get_instance(a_value);
92- self.is_data_null := ut_metadata.is_anytype_null(a_value,self.compound_type);
93- self.data_id := sys_guid();
94- self.self_type := $$plsql_unit;
95- self.cursor_details := ut_cursor_details();
96-
97- ut_compound_data_helper.cleanup_diff;
98-
99- if not self.is_null() then
100- self.extract_path := get_extract_path(a_value);
101- get_cursor_from_anydata(a_value,l_refcursor);
139+ execute immediate l_anydata_sql using in a_value, in out l_refcursor;
140+
102141 if l_refcursor%isopen then
103- self.elements_count := self. extract_cursor(l_refcursor);
142+ extract_cursor(l_refcursor);
104143 l_cursor_number := dbms_sql.to_cursor_number(l_refcursor);
105144 self.cursor_details := ut_cursor_details(l_cursor_number);
106145 dbms_sql.close_cursor(l_cursor_number);
@@ -109,7 +148,6 @@ create or replace type body ut_data_value_anydata as
109148 end if;
110149 end if;
111150
112-
113151 exception
114152 when cursor_not_open then
115153 raise_application_error(-20155, 'Cursor is not open');
0 commit comments