Skip to content

Commit ace6748

Browse files
author
Jacek Gębal
committed
Filtering by tags fixed for schemas containing suite-paths and contexts
Resolves: #1324
1 parent 2742ab4 commit ace6748

6 files changed

Lines changed: 132 additions & 101 deletions

File tree

source/core/ut_suite_tag_filter.pkb

Lines changed: 91 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,28 @@ create or replace package body ut_suite_tag_filter is
1919
/**
2020
* Constants use in postfix and infix transformations
2121
*/
22-
gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!');
22+
gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!');
2323
gc_unary_operators constant ut_varchar2_list := ut_varchar2_list('!'); -- right side associative operator
2424
gc_binary_operators constant ut_varchar2_list := ut_varchar2_list('|','&'); -- left side associative operator
2525
gc_reserved_tag_words constant ut_varchar2_list := ut_varchar2_list('none','any');
26-
gc_tags_column_name constant varchar2(250) := 'tags';
2726
gc_exception_msg constant varchar2(200) := 'Invalid tag expression';
28-
29-
type t_precedence_table is table of number index by varchar2(1);
30-
g_precedence t_precedence_table;
27+
28+
type t_precedence_table is table of number index by varchar2(1);
29+
g_precedence t_precedence_table;
3130

3231
function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list is
33-
l_tags_tokens ut_varchar2_list := ut_varchar2_list();
32+
l_tags_tokens ut_varchar2_list := ut_varchar2_list();
3433
begin
3534
--Tokenize a string into operators and tags
3635
select regexp_substr(a_tags,'([^!()|&]+)|([!()|&])', 1, level) as string_parts
3736
bulk collect into l_tags_tokens
3837
from dual connect by regexp_substr (a_tags, '([^!()|&]+)|([!()|&])', 1, level) is not null;
39-
38+
4039
return l_tags_tokens;
4140
end;
4241

4342
/*
44-
To support a legact tag notation
43+
To support a legact tag notation
4544
, = OR
4645
- = NOT
4746
we will perform a replace of that characters into
@@ -57,36 +56,36 @@ create or replace package body ut_suite_tag_filter is
5756
l_tags_exclude varchar2(4000);
5857
l_return_tag varchar2(4000);
5958
begin
60-
if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then
59+
if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then
6160

6261
select '('||listagg( t.column_value,'|')
63-
within group( order by column_value)||')'
62+
within group( order by column_value)||')'
6463
into l_tags_include
6564
from table(l_tags) t
6665
where t.column_value not like '-%';
67-
66+
6867
select '('||listagg( replace(t.column_value,'-','!'),' & ')
6968
within group( order by column_value)||')'
7069
into l_tags_exclude
7170
from table(l_tags) t
72-
where t.column_value like '-%';
73-
71+
where t.column_value like '-%';
72+
7473

7574
l_return_tag:=
76-
case
75+
case
7776
when l_tags_include <> '()' and l_tags_exclude <> '()'
7877
then l_tags_include || ' & ' || l_tags_exclude
7978
when l_tags_include <> '()'
8079
then l_tags_include
8180
when l_tags_exclude <> '()'
82-
then l_tags_exclude
81+
then l_tags_exclude
8382
end;
84-
else
83+
else
8584
l_return_tag := a_tags;
86-
end if;
85+
end if;
8786
return l_return_tag;
8887
end;
89-
88+
9089
/*
9190
https://stackoverflow.com/questions/29634992/shunting-yard-validate-expression
9291
*/
@@ -97,14 +96,14 @@ create or replace package body ut_suite_tag_filter is
9796
l_expect_operand boolean := true;
9897
l_expect_operator boolean := false;
9998
l_idx pls_integer;
100-
begin
99+
begin
101100
l_idx := a_tags.first;
102101
--Exuecute modified shunting algorithm
103102
WHILE (l_idx is not null) loop
104103
l_token := a_tags(l_idx);
105104
if (l_token member of gc_operators and l_token member of gc_binary_operators) then
106-
if not(l_expect_operator) then
107-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
105+
if not(l_expect_operator) then
106+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
108107
end if;
109108
while l_operator_stack.top > 0 and (g_precedence(l_operator_stack.peek) > g_precedence(l_token)) loop
110109
l_rnp_tokens.extend;
@@ -113,56 +112,56 @@ create or replace package body ut_suite_tag_filter is
113112
l_operator_stack.push(a_tags(l_idx));
114113
l_expect_operand := true;
115114
l_expect_operator:= false;
116-
elsif (l_token member of gc_operators and l_token member of gc_unary_operators) then
117-
if not(l_expect_operand) then
118-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
119-
end if;
115+
elsif (l_token member of gc_operators and l_token member of gc_unary_operators) then
116+
if not(l_expect_operand) then
117+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
118+
end if;
120119
l_operator_stack.push(a_tags(l_idx));
121120
l_expect_operand := true;
122-
l_expect_operator:= false;
121+
l_expect_operator:= false;
123122
elsif l_token = '(' then
124-
if not(l_expect_operand) then
125-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
126-
end if;
123+
if not(l_expect_operand) then
124+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
125+
end if;
127126
l_operator_stack.push(a_tags(l_idx));
128127
l_expect_operand := true;
129-
l_expect_operator:= false;
128+
l_expect_operator:= false;
130129
elsif l_token = ')' then
131-
if not(l_expect_operator) then
132-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
133-
end if;
130+
if not(l_expect_operator) then
131+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
132+
end if;
134133
while l_operator_stack.peek <> '(' loop
135134
l_rnp_tokens.extend;
136135
l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop;
137136
end loop;
138137
l_operator_stack.pop; --Pop the open bracket and discard it
139138
l_expect_operand := false;
140-
l_expect_operator:= true;
139+
l_expect_operator:= true;
141140
else
142-
if not(l_expect_operand) then
143-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
141+
if not(l_expect_operand) then
142+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
144143
end if;
145144
l_rnp_tokens.extend;
146145
l_rnp_tokens(l_rnp_tokens.last) :=l_token;
147146
l_expect_operator := true;
148147
l_expect_operand := false;
149148
end if;
150-
149+
151150
l_idx := a_tags.next(l_idx);
152151
end loop;
153-
152+
154153
while l_operator_stack.peek is not null loop
155-
if l_operator_stack.peek in ('(',')') then
156-
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
157-
end if;
154+
if l_operator_stack.peek in ('(',')') then
155+
raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg);
156+
end if;
158157
l_rnp_tokens.extend;
159-
l_rnp_tokens(l_rnp_tokens.last):=l_operator_stack.pop;
158+
l_rnp_tokens(l_rnp_tokens.last):=l_operator_stack.pop;
160159
end loop;
161-
160+
162161
return l_rnp_tokens;
163162
end shunt_logical_expression;
164-
165-
function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2)
163+
164+
function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2)
166165
return varchar2 is
167166
l_infix_stack ut_stack := ut_stack();
168167
l_right_side varchar2(32767);
@@ -175,22 +174,22 @@ create or replace package body ut_suite_tag_filter is
175174
while ( l_idx is not null) loop
176175
--If the token we got is a none or any keyword
177176
if a_postfix_exp(l_idx) member of gc_reserved_tag_words then
178-
179-
l_infix_stack.push(
180-
case
177+
178+
l_infix_stack.push(
179+
case
181180
when a_postfix_exp(l_idx) = 'none' then '('||a_tags_column_name||' is empty or '||a_tags_column_name||' is null)'
182181
else a_tags_column_name||' is not empty'
183182
end
184183
);
185184
--If token is operand but also single tag
186185
elsif regexp_count(a_postfix_exp(l_idx),'[!()|&]') = 0 then
187186
l_infix_stack.push(q'[']'||a_postfix_exp(l_idx)||q'[']'||l_member_token);
188-
--If token is unary operator not
187+
--If token is unary operator not
189188
elsif a_postfix_exp(l_idx) member of gc_unary_operators then
190189
l_right_side := l_infix_stack.pop;
191190
l_infix_exp := a_postfix_exp(l_idx)||'('||l_right_side||')';
192191
l_infix_stack.push(l_infix_exp);
193-
--If token is binary operator
192+
--If token is binary operator
194193
elsif a_postfix_exp(l_idx) member of gc_binary_operators then
195194
l_right_side := l_infix_stack.pop;
196195
l_left_side := l_infix_stack.pop;
@@ -199,7 +198,7 @@ create or replace package body ut_suite_tag_filter is
199198
end if;
200199
l_idx := a_postfix_exp.next(l_idx);
201200
end loop;
202-
201+
203202
return l_infix_stack.pop;
204203
end conv_postfix_to_infix_sql;
205204

@@ -208,73 +207,69 @@ create or replace package body ut_suite_tag_filter is
208207
l_tags varchar2(4000);
209208
begin
210209
l_tags := replace(replace_legacy_tag_notation(a_tags),' ');
211-
l_tags := conv_postfix_to_infix_sql(shunt_logical_expression(tokenize_tags_string(l_tags)),gc_tags_column_name);
210+
l_tags := conv_postfix_to_infix_sql(shunt_logical_expression(tokenize_tags_string(l_tags)),'tags');
212211
l_tags := replace(l_tags, '|',' or ');
213212
l_tags := replace(l_tags ,'&',' and ');
214213
l_tags := replace(l_tags ,'!','not');
215-
return l_tags;
216-
end;
214+
return l_tags;
215+
end;
217216

218217

219218
/*
220219
Having a base set of suites we will do a further filter down if there are
221220
any tags defined.
222-
*/
221+
*/
223222
function get_tags_suites (
224223
a_suite_items ut_suite_cache_rows,
225224
a_tags varchar2
226225
) return ut_suite_cache_rows is
227-
l_suite_tags ut_suite_cache_rows := ut_suite_cache_rows();
226+
l_results ut_suite_cache_rows;
228227
l_sql varchar2(32000);
229-
l_tags varchar2(4000):= create_where_filter(a_tags);
228+
l_tags_filter_clause varchar2(4000):= create_where_filter(a_tags);
230229
begin
231230
l_sql :=
232231
q'[
233-
with
234-
suites_mv as (
235-
select c.id,value(c) as obj,c.path as path,c.self_type,c.object_owner,c.tags as ]'||gc_tags_column_name||q'[
236-
from table(:suite_items) c
237-
),
238-
suites_matching_expr as (
239-
select c.id,c.path as path,c.self_type,c.object_owner,c.tags
240-
from suites_mv c
241-
where c.self_type in ('UT_SUITE','UT_CONTEXT')
242-
and ]'||l_tags||q'[
243-
),
244-
tests_matching_expr as (
245-
select c.id,c.path as path,c.self_type,c.object_owner,c.tags as ]'||gc_tags_column_name||q'[
246-
from suites_mv c where c.self_type in ('UT_TEST')
247-
and ]'||l_tags||q'[
248-
),
249-
tests_with_tags_inh_from_suite as (
250-
select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner
251-
from suites_mv c join suites_matching_expr t
252-
on (c.path||'.' like t.path || '.%' /*all descendants and self*/ and c.object_owner = t.object_owner)
253-
),
254-
tests_with_tags_prom_to_suite as (
255-
select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner
256-
from suites_mv c join tests_matching_expr t
257-
on (t.path||'.' like c.path || '.%' /*all ancestors and self*/ and c.object_owner = t.object_owner)
258-
)
259-
select obj from suites_mv c,
260-
(select id,row_number() over (partition by id order by id) r_num from
261-
(select id
262-
from tests_with_tags_prom_to_suite tst
263-
where ]'||l_tags||q'[
264-
union all
265-
select id from tests_with_tags_inh_from_suite tst
266-
where ]'||l_tags||q'[
267-
)
268-
) t where c.id = t.id and r_num = 1 ]';
269-
execute immediate l_sql bulk collect into l_suite_tags using a_suite_items;
270-
return l_suite_tags;
232+
with
233+
suites_mv as (
234+
select c.id,value(c) as obj,c.path as path,c.self_type,c.object_owner,c.tags as tags
235+
from table(:suite_items) c
236+
),
237+
propagate_tags_from_ancestors as (
238+
select c.id, c.self_type, c.path, c.object_owner, cast(collect(c_tags.tag) as ut_varchar2_rows) as tags
239+
from suites_mv c
240+
left join suites_mv t
241+
on t.self_type in ('UT_SUITE','UT_SUITE_CONTEXT')
242+
and c.path like t.path||'.%' /* all descendants */
243+
and c.object_owner = t.object_owner
244+
outer apply (select column_value tag from table(c.tags) union select column_value tag from table(t.tags) ) c_tags
245+
group by c.id, c.self_type, c.path, c.object_owner
246+
),
247+
filter_by_tags as (
248+
select *
249+
from propagate_tags_from_ancestors x
250+
where ( ]'||l_tags_filter_clause||q'[ )
251+
),
252+
only_items_with_tests as (
253+
select item.obj
254+
from suites_mv item
255+
where exists (
256+
select 1
257+
from filter_by_tags tst
258+
where tst.self_type in ('UT_TEST')
259+
and (tst.path||'.' like item.path || '.%' /*all descendants and self*/ and item.object_owner = tst.object_owner)
260+
)
261+
)
262+
select obj from only_items_with_tests]';
263+
execute immediate l_sql bulk collect into l_results using a_suite_items;
264+
265+
return l_results;
271266
end;
272267

273268
function apply(
274269
a_unfiltered_rows ut_suite_cache_rows,
275270
a_tags varchar2 := null
276271
) return ut_suite_cache_rows is
277-
l_suite_items ut_suite_cache_rows := a_unfiltered_rows;
272+
l_suite_items ut_suite_cache_rows := a_unfiltered_rows;
278273
begin
279274
if length(a_tags) > 0 then
280275
l_suite_items := get_tags_suites(l_suite_items,a_tags);

test/install_and_run_tests.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#!/bin/bash
22
set -ev
33

4-
. ./development/env.sh
5-
64
#goto git root directory
75
git rev-parse && cd "$(git rev-parse --show-cdup)"
6+
. ./development/env.sh
87
cd test
98

109
time . ./install_tests.sh

test/install_tests.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/bin/bash
22
set -ev
33

4-
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
5-
cd ${SCRIPT_DIR}
4+
git rev-parse && cd "$(git rev-parse --show-cdup)"
5+
. ./development/env.sh
6+
cd test
67

78

89
"$SQLCLI" UT3_TESTER_HELPER/ut3@//${CONNECTION_STR} @install_ut3_tester_helper.sql

test/run_tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set -ev
33

44
#goto git root directory
55
git rev-parse && cd "$(git rev-parse --show-cdup)"
6+
. ./development/env.sh
67

78
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
89

0 commit comments

Comments
 (0)