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

Skip to content

Commit 86a4f09

Browse files
committed
Fixes to compare when it trying to get anydata out of collection
Updated documentation
1 parent 46ac6a2 commit 86a4f09

7 files changed

Lines changed: 465 additions & 36 deletions

File tree

docs/userguide/advanced_data_comparison.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ You can now join two cursors by defining a primary key or composite key that wil
143143

144144
Join by option can be used in conjunction with include or exclude options. However if any of the join keys is part of exclude set, comparison will fail and report to user that sets could not be joined on specific key (excluded).
145145

146-
Join by options currently doesn't support nested table inside cursor.
147-
148146
```sql
149147
procedure join_by_username is
150148
l_actual sys_refcursor;
@@ -171,6 +169,57 @@ This will show you difference in row 'TEST' regardless of order.
171169

172170
Assumption is that join by is made by column name so that what will be displayed as part of results.
173171

172+
Join by options currently doesn't support nested table inside cursor comparison, however is still possible to compare a collection as a whole.
173+
174+
Example.
175+
176+
```sql
177+
procedure compare_collection_in_rec is
178+
l_actual sys_refcursor;
179+
l_expected sys_refcursor;
180+
l_actual_tab ut3.ut_annotated_object;
181+
l_expected_tab ut3.ut_annotated_object;
182+
l_expected_message varchar2(32767);
183+
l_actual_message varchar2(32767);
184+
begin
185+
select ut3.ut_annotated_object('TEST','TEST','TEST',
186+
ut3.ut_annotations(ut3.ut_annotation(1,'test','test','test'),
187+
ut3.ut_annotation(2,'test','test','test'))
188+
)
189+
into l_actual_tab from dual;
190+
191+
select ut3.ut_annotated_object('TEST','TEST','TEST',
192+
ut3.ut_annotations(ut3.ut_annotation(1,'test','test','test'),
193+
ut3.ut_annotation(2,'test','test','test'))
194+
)
195+
into l_expected_tab from dual;
196+
197+
--Arrange
198+
open l_actual for select l_actual_tab as nested_table from dual;
199+
200+
open l_expected for select l_expected_tab as nested_table from dual;
201+
202+
--Act
203+
ut3.ut.expect(l_actual).to_equal(l_expected).join_by('NESTED_TABLE/ANNOTATIONS');
204+
205+
end;
206+
```
207+
208+
209+
210+
In case when a there is detected collection inside cursor and we cannot join key. Comparison will present a failed joins and also a message about collection being detected.
211+
212+
```sql
213+
Actual: refcursor [ count = 1 ] was expected to equal: refcursor [ count = 1 ]
214+
Diff:
215+
Unable to join sets:
216+
Join key NESTED_TABLE/ANNOTATIONS/TEXT does not exists in expected
217+
Join key NESTED_TABLE/ANNOTATIONS/TEXT does not exists in actual
218+
Please make sure that your join clause is not refferring to collection element
219+
```
220+
221+
222+
174223

175224

176225
**Please note that .join_by option will take longer to process due to need of parsing via primary keys.**

source/expectations/data_values/ut_curr_usr_compound_helper.pkb

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
11
create or replace package body ut_curr_usr_compound_helper is
22

33
type t_type_name_map is table of varchar2(100) index by binary_integer;
4-
g_type_name_map t_type_name_map;
5-
g_anytype_name_map t_type_name_map;
6-
g_user_defined_type pls_integer := dbms_sql.user_defined_type;
7-
4+
g_type_name_map t_type_name_map;
5+
g_anytype_name_map t_type_name_map;
6+
g_anytype_collection_name t_type_name_map;
7+
g_user_defined_type pls_integer := dbms_sql.user_defined_type;
8+
g_is_collection boolean := false;
9+
10+
procedure set_collection_state(a_is_collection boolean) is
11+
begin
12+
--Make sure that we set a g_is_collection only once so we dont reset from true to false.
13+
if not g_is_collection then
14+
g_is_collection := a_is_collection;
15+
end if;
16+
end;
817

918
function get_column_type(a_desc_rec dbms_sql.desc_rec3,a_desc_user_types boolean := false) return ut_key_anyval_pair is
1019
l_data ut_data_value;
1120
l_result ut_key_anyval_pair;
12-
l_data_type varchar2(500) := 'unknown datatype';
21+
l_data_type varchar2(500) := 'unknown datatype';
22+
23+
function is_collection (a_owner varchar2,a_type_name varchar2) return boolean is
24+
l_type_view varchar2(200) := ut_metadata.get_dba_view('dba_types');
25+
l_typecode varchar2(100);
26+
begin
27+
execute immediate 'select typecode from '||l_type_view ||'
28+
where owner = :owner and type_name = :typename'
29+
into l_typecode using a_owner,a_type_name;
30+
31+
return l_typecode = 'COLLECTION';
32+
end;
33+
1334
begin
1435
if g_type_name_map.exists(a_desc_rec.col_type) then
1536
l_data := ut_data_value_varchar2(g_type_name_map(a_desc_rec.col_type));
37+
/*If its a collection regardless is we want to describe user defined types we will return just a name
38+
and capture that name */
39+
elsif a_desc_rec.col_type = g_user_defined_type and is_collection(a_desc_rec.col_schema_name,a_desc_rec.col_type_name) then
40+
l_data := ut_data_value_varchar2(a_desc_rec.col_schema_name||'.'||a_desc_rec.col_type_name);
41+
set_collection_state(true);
1642
elsif a_desc_rec.col_type = g_user_defined_type and a_desc_user_types then
1743
l_data :=ut_data_value_xmltype(get_user_defined_type(a_desc_rec.col_schema_name,a_desc_rec.col_type_name));
1844
elsif a_desc_rec.col_schema_name is not null and a_desc_rec.col_type_name is not null then
@@ -47,6 +73,52 @@ create or replace package body ut_curr_usr_compound_helper is
4773
return l_columns_tab;
4874
end;
4975

76+
procedure get_descr_cursor(a_cursor in out nocopy sys_refcursor,a_columns_tab in out ut_key_anyval_pairs,
77+
a_join_by_tab in out ut_key_anyval_pairs) is
78+
l_cursor_number integer;
79+
l_columns_count pls_integer;
80+
l_columns_desc dbms_sql.desc_tab3;
81+
begin
82+
if a_cursor is null or not a_cursor%isopen then
83+
a_columns_tab := null;
84+
a_join_by_tab := null;
85+
end if;
86+
l_cursor_number := dbms_sql.to_cursor_number( a_cursor );
87+
dbms_sql.describe_columns3( l_cursor_number, l_columns_count, l_columns_desc );
88+
a_cursor := dbms_sql.to_refcursor( l_cursor_number );
89+
a_columns_tab := get_columns_info( l_columns_desc, l_columns_count,false);
90+
a_join_by_tab := get_columns_info( l_columns_desc, l_columns_count,true);
91+
end;
92+
93+
procedure get_columns_info(a_cursor in out nocopy sys_refcursor,a_columns_info out xmltype,
94+
a_join_by_info out xmltype, a_contains_collection out number) is
95+
l_columns_info xmltype;
96+
l_join_by_info xmltype;
97+
l_result_tmp xmltype;
98+
l_columns_tab ut_key_anyval_pairs;
99+
l_join_by_tab ut_key_anyval_pairs;
100+
begin
101+
102+
get_descr_cursor(a_cursor,l_columns_tab,l_join_by_tab);
103+
104+
for i in 1..l_columns_tab.COUNT
105+
loop
106+
l_result_tmp := ut_compound_data_helper.get_column_info_xml(l_columns_tab(i));
107+
select xmlconcat(l_columns_info,l_result_tmp) into l_columns_info from dual;
108+
end loop;
109+
110+
for i in 1..l_join_by_tab.COUNT
111+
loop
112+
l_result_tmp := ut_compound_data_helper.get_column_info_xml(l_join_by_tab(i));
113+
select xmlconcat(l_join_by_info,l_result_tmp) into l_join_by_info from dual;
114+
end loop;
115+
116+
select XMLELEMENT("ROW",l_columns_info ),XMLELEMENT("ROW",l_join_by_info )
117+
into a_columns_info,a_join_by_info from dual;
118+
119+
a_contains_collection := ut_utils.boolean_to_int(g_is_collection);
120+
end;
121+
50122
function get_columns_info(a_cursor in out nocopy sys_refcursor,a_desc_user_types boolean := false) return xmltype is
51123
l_result xmltype;
52124
l_result_tmp xmltype;
@@ -115,7 +187,10 @@ create or replace package body ut_curr_usr_compound_helper is
115187

116188
l_result.extend;
117189
l_result(l_result.last) := ut_key_value_pair(l_aname, g_anytype_name_map(l_attribute_typecode));
118-
190+
--check for collection
191+
if g_anytype_collection_name.exists(l_attribute_typecode) then
192+
set_collection_state(true);
193+
end if;
119194
end loop;
120195
return l_result;
121196
end;
@@ -126,7 +201,8 @@ create or replace package body ut_curr_usr_compound_helper is
126201
l_typecode pls_integer;
127202
l_result xmltype;
128203
l_columns_tab ut_key_value_pairs := ut_key_value_pairs();
129-
begin
204+
205+
begin
130206
execute immediate 'declare
131207
l_v '||a_owner||'.'||a_type_name||';
132208
begin
@@ -138,7 +214,7 @@ create or replace package body ut_curr_usr_compound_helper is
138214

139215
select xmlagg(xmlelement(evalname key,value))
140216
into l_result from table(l_columns_tab);
141-
217+
142218
return l_result;
143219

144220
end;
@@ -161,6 +237,14 @@ create or replace package body ut_curr_usr_compound_helper is
161237
g_anytype_name_map(dbms_types.typecode_bfloat) := 'BINARY_FLOAT';
162238
g_anytype_name_map(dbms_types.typecode_bdouble) := 'BINARY_DOUBLE';
163239
g_anytype_name_map(dbms_types.typecode_urowid) := 'UROWID';
240+
g_anytype_name_map(dbms_types.typecode_varray) := 'VARRRAY';
241+
g_anytype_name_map(dbms_types.typecode_table) := 'TABLE';
242+
g_anytype_name_map(dbms_types.typecode_namedcollection) := 'NAMEDCOLLECTION';
243+
244+
g_anytype_collection_name(dbms_types.typecode_varray) := 'VARRRAY';
245+
g_anytype_collection_name(dbms_types.typecode_table) := 'TABLE';
246+
g_anytype_collection_name(dbms_types.typecode_namedcollection) := 'NAMEDCOLLECTION';
247+
164248

165249
g_type_name_map( dbms_sql.binary_bouble_type ) := 'BINARY_DOUBLE';
166250
g_type_name_map( dbms_sql.bfile_type ) := 'BFILE';
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
create or replace package ut_curr_usr_compound_helper authid current_user is
22

3-
function get_columns_info(a_cursor in out nocopy sys_refcursor,a_desc_user_types boolean := false) return xmltype;
3+
procedure get_columns_info(a_cursor in out nocopy sys_refcursor,a_columns_info out xmltype,
4+
a_join_by_info out xmltype, a_contains_collection out number);
45

5-
function get_user_defined_type(a_owner varchar2,a_type_name varchar2) return xmltype;
6+
function get_columns_info(a_cursor in out nocopy sys_refcursor,
7+
a_desc_user_types boolean := false) return xmltype;
8+
9+
function get_user_defined_type(a_owner varchar2,a_type_name varchar2) return xmltype;
610

711
end;
812
/

source/expectations/data_values/ut_data_value_refcursor.tpb

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ create or replace type body ut_data_value_refcursor as
3737
self.data_type := 'refcursor';
3838
if l_cursor is not null then
3939
if l_cursor%isopen then
40-
self.columns_info := ut_curr_usr_compound_helper.get_columns_info(l_cursor);
41-
self.key_info := ut_curr_usr_compound_helper.get_columns_info(l_cursor,true);
40+
--Get some more info regarding cursor, including if it containts collection columns and what is their name
41+
42+
ut_curr_usr_compound_helper.get_columns_info(l_cursor,self.columns_info,self.key_info,
43+
self.contain_collection);
44+
4245
self.elements_count := 0;
4346
-- We use DBMS_XMLGEN in order to:
4447
-- 1) be able to process data in bulks (set of rows)
@@ -134,14 +137,16 @@ create or replace type body ut_data_value_refcursor as
134137
end;
135138

136139
function get_missing_key_message(a_missing_keys ut_compound_data_helper.t_missing_pk) return varchar2 is
140+
l_message varchar2(200);
137141
begin
138-
return
139-
case a_missing_keys.diff_type
140-
when 'a' then
141-
' Join key '||a_missing_keys.missingxpath||' does not exists in actual'
142-
when 'e' then
143-
' Join key '||a_missing_keys.missingxpath||' does not exists in expected'
144-
end;
142+
143+
if a_missing_keys.diff_type = 'a' then
144+
l_message := ' Join key '||a_missing_keys.missingxpath||' does not exists in actual';
145+
elsif a_missing_keys.diff_type = 'e' then
146+
l_message := ' Join key '||a_missing_keys.missingxpath||' does not exists in expected';
147+
end if;
148+
149+
return l_message;
145150
end;
146151

147152
function add_incomparable_cols_to_xpath(
@@ -202,7 +207,12 @@ create or replace type body ut_data_value_refcursor as
202207
for i in 1 .. l_missing_pk.count loop
203208
l_results.extend;
204209
ut_utils.append_to_clob(l_result, get_missing_key_message(l_missing_pk(i))|| chr(10));
205-
end loop;
210+
end loop;
211+
212+
if ut_utils.int_to_boolean(self.contain_collection) or ut_utils.int_to_boolean(l_actual.contain_collection) then
213+
ut_utils.append_to_clob(l_result,' Please make sure that your join clause is not refferring to collection element'|| chr(10));
214+
end if;
215+
206216
end if;
207217

208218
l_result_string := ut_utils.to_string(l_result,null);

source/expectations/data_values/ut_data_value_refcursor.tps

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ create or replace type ut_data_value_refcursor under ut_compound_data_value(
2424
* Determines if the cursor is null
2525
*/
2626
is_cursor_null integer,
27-
27+
28+
/**
29+
* hold information if the cursor contains collection object
30+
*/
31+
contain_collection number(1,0),
32+
2833
/**
2934
* Holds information about column names and column data-types
3035
*/

0 commit comments

Comments
 (0)