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

Skip to content

Commit 147a506

Browse files
committed
Fixing issues with null and empty objects / collections
1 parent 7bb5533 commit 147a506

12 files changed

Lines changed: 464 additions & 56 deletions

docs/userguide/expectations.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,60 @@ drop type departments;
941941
drop type department;
942942
```
943943

944+
Some of the possible combinations of the anydata and their results:
945+
946+
```sql
947+
create or replace type t_tab_varchar is table of varchar2(1)
948+
/
949+
950+
create or replace type dummy_obj as object (
951+
id number,
952+
"name" varchar2(30),
953+
"Value" varchar2(30)
954+
)
955+
/
956+
957+
create or replace type dummy_obj_lst as table of dummy_obj
958+
/
959+
960+
create or replace type t_varray is varray(1) of number
961+
/
962+
963+
```
964+
965+
966+
967+
968+
969+
| Type A | Comparisoon | Type B | Result |
970+
| :------------------------------------- | :-----------: | :------------------------------------ | -----: |
971+
| t_tab_varchar('A') | equal | t_tab_varchar('A') | Pass |
972+
| t_tab_varchar('A') | equal | t_tab_varchar('B') | Fail |
973+
| t_tab_varchar | is_null | | Pass |
974+
| t_tab_varchar | equal | t_tab_varchar | Pass |
975+
| t_tab_varchar | equal | t_tab_varchar('A') | Fail |
976+
| t_tab_varchar() | have_count(0) | | Pass |
977+
| t_tab_varchar() | equal | t_tab_varchar() | Pass |
978+
| t_tab_varchar() | equal | t_tab_varchar('A') | Fail |
979+
| dummy_obj_lst (dummy_obj(1, 'A', '0')) | equal | dummy_obj_lst(dummy_obj(1, 'A', '0')) | Pass |
980+
| dummy_obj_lst (dummy_obj(1, 'A', '0')) | equal | dummy_obj_lst(dummy_obj(2, 'A', '0')) | Fail |
981+
| dummy_obj_lst | equal | dummy_obj_lst(dummy_obj(1, 'A', '0')) | Fail |
982+
| dummy_obj_lst | is_null | | Pass |
983+
| dummy_obj_lst | equal | dummy_obj_lst | Pass |
984+
| dummy_obj_lst() | have_count(0) | | Pass |
985+
| dummy_obj_lst() | equal | dummy_obj_lst(dummy_obj(1, 'A', '0')) | Fail |
986+
| dummy_obj_lst() | equal | dummy_obj_lst() | Pass |
987+
| t_varray | is null | | Pass |
988+
| t_varray | equal | t_varray | Pass |
989+
| t_varray | equal | t_varray(1) | Fail |
990+
| t_varray() | have_count(0) | | Pass |
991+
| t_varray() | equal | t_varray() | Pass |
992+
| t_varray() | equal | t_varray(1) | Fail |
993+
| t_varray(1) | equal | t_varray(1) | Pass |
994+
| t_varray(1) | equal | t_varray(2) | Fail |
995+
996+
997+
944998
### Comparing cursor data containing DATE fields
945999

9461000
**Important note**

source/core/ut_metadata.pkb

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,63 @@ create or replace package body ut_metadata as
274274
function get_collection_element(a_anydata in anydata) return varchar2
275275
is
276276
l_anytype anytype;
277-
l_nested_type t_anytype_members_rec;
277+
l_nested_type t_anytype_members_rec;
278+
l_elements_rec t_anytype_elem_info_rec;
278279
l_type_code integer;
279280
begin
280281
l_type_code := a_anydata.gettype(l_anytype);
281282
if is_collection(l_type_code) then
282-
l_nested_type := get_anytype_members_info(ut_metadata.get_attr_elem_info(l_anytype).attr_elt_type);
283+
l_elements_rec := get_attr_elem_info(l_anytype);
284+
if l_elements_rec.attr_elt_type is null then
285+
l_nested_type := get_anytype_members_info(l_anytype);
286+
else
287+
l_nested_type := get_anytype_members_info(l_elements_rec.attr_elt_type);
288+
end if;
283289
end if;
284290
return l_nested_type.schema_name || '.' ||l_nested_type.type_name;
291+
end;
292+
293+
function has_collection_members (a_anydata in anydata) return boolean is
294+
l_anytype anytype;
295+
l_nested_type t_anytype_members_rec;
296+
l_elements_rec t_anytype_elem_info_rec;
297+
l_type_code integer;
298+
begin
299+
l_type_code := a_anydata.gettype(l_anytype);
300+
l_elements_rec := get_attr_elem_info(l_anytype);
301+
if l_elements_rec.attr_elt_type is null then
302+
return false;
303+
else
304+
return true;
305+
end if;
285306
end;
286307

308+
function get_anydata_typename(a_data_value anydata) return varchar2
309+
is
310+
begin
311+
return case when a_data_value is not null then lower(a_data_value.gettypename()) else 'undefined' end;
312+
end;
313+
314+
function is_anytype_null(a_value in anydata, a_compound_type in varchar2) return number is
315+
l_result integer := 0;
316+
l_anydata_sql varchar2(4000);
317+
begin
318+
if a_value is not null then
319+
l_anydata_sql := '
320+
declare
321+
l_data '||get_anydata_typename(a_value)||';
322+
l_value anydata := :a_value;
323+
l_status integer;
324+
begin
325+
l_status := l_value.get'||a_compound_type||'(l_data);
326+
:l_data_is_null := case when l_data is null then 1 else 0 end;
327+
end;';
328+
execute immediate l_anydata_sql using in a_value, out l_result;
329+
else
330+
l_result := 1;
331+
end if;
332+
return l_result;
333+
end;
334+
287335
end;
288336
/

source/core/ut_metadata.pks

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ create or replace package ut_metadata authid current_user as
151151
* Return fully qualified name of the object from collection, if not collection returns null
152152
*/
153153
function get_collection_element(a_anydata in anydata) return varchar2;
154+
155+
/**
156+
* Check if collection got elements
157+
*/
158+
function has_collection_members (a_anydata in anydata) return boolean;
159+
160+
/**
161+
* Get typename from anydata
162+
*/
163+
function get_anydata_typename(a_data_value anydata) return varchar2;
164+
165+
/**
166+
* Is anydata object/collection is null
167+
*/
168+
function is_anytype_null(a_value in anydata, a_compound_type in varchar2) return number;
154169

155170
end ut_metadata;
156171
/

source/expectations/data_values/ut_data_value_anydata.tpb

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,51 +15,16 @@ create or replace type body ut_data_value_anydata as
1515
See the License for the specific language governing permissions and
1616
limitations under the License.
1717
*/
18-
19-
member function is_null(a_value in anydata) return number is
20-
l_result integer := 0;
21-
l_anydata_sql varchar2(4000);
22-
begin
23-
if a_value is not null and self.compound_type = 'object' then
24-
l_anydata_sql := '
25-
declare
26-
l_data '||self.data_type||';
27-
l_value anydata := :a_value;
28-
l_status integer;
29-
begin
30-
l_status := l_value.get'||self.compound_type||'(l_data);
31-
:l_data_is_null := case when l_data is null then 1 else 0 end;
32-
end;';
33-
execute immediate l_anydata_sql using in a_value, out l_result;
34-
--TODO : Refactor
35-
elsif a_value is not null and self.compound_type = 'collection' then
36-
l_anydata_sql := '
37-
declare
38-
l_data '||self.data_type||';
39-
l_value anydata := :a_value;
40-
l_status integer;
41-
begin
42-
l_status := l_value.get'||self.compound_type||'(l_data);
43-
:l_data_is_null := case
44-
when l_data is null then 1
45-
when l_data is empty then 1
46-
else 0 end;
47-
end;';
48-
execute immediate l_anydata_sql using in a_value, out l_result;
49-
else
50-
l_result := 1;
51-
end if;
52-
return l_result;
53-
end;
5418

5519
overriding member function get_object_info return varchar2 is
5620
begin
5721
return self.data_type || case when self.compound_type = 'collection' then ' [ count = '||self.elements_count||' ]' else null end;
5822
end;
59-
23+
6024
member procedure get_cursor_from_anydata(a_value in anydata, a_refcursor out sys_refcursor) is
6125
l_anydata_sql varchar2(4000);
62-
26+
l_cursor_sql varchar2(2000);
27+
6328
function resolve_name(a_object_name in varchar2) return varchar2 is
6429
l_schema varchar(250);
6530
l_object varchar(250);
@@ -79,7 +44,7 @@ create or replace type body ut_data_value_anydata as
7944
return resolve_name(a_datatype);
8045
end;
8146

82-
begin
47+
begin
8348
l_anydata_sql := '
8449
declare
8550
l_data '||self.data_type||';
@@ -98,7 +63,22 @@ create or replace type body ut_data_value_anydata as
9863
end;';
9964
execute immediate l_anydata_sql using in a_value, out a_refcursor;
10065
end;
101-
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+
10282
member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata) is
10383
l_refcursor sys_refcursor;
10484
l_ctx number;
@@ -107,20 +87,18 @@ create or replace type body ut_data_value_anydata as
10787
l_cursor_number number;
10888

10989
begin
110-
self.data_type := case when a_value is not null then lower(a_value.gettypename()) else 'undefined' end;
90+
self.data_type := ut_metadata.get_anydata_typename(a_value);
11191
self.compound_type := get_instance(a_value);
112-
self.extract_path := '/*/*';
92+
self.is_data_null := ut_metadata.is_anytype_null(a_value,self.compound_type);
11393
self.data_id := sys_guid();
11494
self.self_type := $$plsql_unit;
11595
self.cursor_details := ut_cursor_details();
116-
self.is_data_null := is_null(a_value);
117-
self.elements_count := 0;
118-
if not self.is_null() then
119-
get_cursor_from_anydata(a_value,l_refcursor);
120-
end if;
12196

12297
ut_compound_data_helper.cleanup_diff;
98+
12399
if not self.is_null() then
100+
self.extract_path := get_extract_path(a_value);
101+
get_cursor_from_anydata(a_value,l_refcursor);
124102
if l_refcursor%isopen then
125103
self.elements_count := self.extract_cursor(l_refcursor);
126104
l_cursor_number := dbms_sql.to_cursor_number(l_refcursor);
@@ -174,6 +152,14 @@ create or replace type body ut_data_value_anydata as
174152
l_result := l_result + (self as ut_data_value_refcursor).compare_implementation(a_other,a_match_options,a_inclusion_compare,a_is_negated);
175153
return l_result;
176154
end;
177-
155+
156+
overriding member function is_empty return boolean is
157+
begin
158+
if self.compound_type = 'collection' then
159+
return self.elements_count = 0;
160+
else
161+
raise value_error;
162+
end if;
163+
end;
178164
end;
179165
/

source/expectations/data_values/ut_data_value_anydata.tps

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ create or replace type ut_data_value_anydata under ut_data_value_refcursor(
1616
limitations under the License.
1717
*/
1818

19-
member function is_null(a_value in anydata) return number,
19+
2020
member procedure get_cursor_from_anydata(a_value in anydata, a_refcursor out sys_refcursor),
2121
overriding member function get_object_info return varchar2,
22+
member function get_extract_path(a_data_value anydata) return varchar2,
2223
member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata),
2324
member function get_instance(a_data_value anydata) return varchar2,
2425
constructor function ut_data_value_anydata(self in out nocopy ut_data_value_anydata, a_value anydata) return self as result,
@@ -27,6 +28,7 @@ create or replace type ut_data_value_anydata under ut_data_value_refcursor(
2728
a_match_options ut_matcher_options,
2829
a_inclusion_compare boolean := false,
2930
a_is_negated boolean := false
30-
) return integer
31+
) return integer,
32+
overriding member function is_empty return boolean
3133
)
32-
34+
/

0 commit comments

Comments
 (0)