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

Skip to content

Commit faf3d12

Browse files
committed
Rework dynamic sql generation to more readible format.
1 parent 80563f8 commit faf3d12

1 file changed

Lines changed: 147 additions & 110 deletions

File tree

source/expectations/data_values/ut_compound_data_helper.pkb

Lines changed: 147 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,54 @@ create or replace package body ut_compound_data_helper is
2121
g_type_name_map t_type_name_map;
2222
g_anytype_name_map t_type_name_map;
2323

24+
gc_compare_sql_template varchar2(4000) :=
25+
q'[
26+
with exp as (
27+
select
28+
ucd.*,
29+
{:duplicate_number:} dup_no
30+
from (
31+
select
32+
ucd.item_data
33+
,x.data_id data_id
34+
,{:item_no:} item_no
35+
{:columns:}
36+
from {:ut3_owner:}.ut_compound_data_tmp x,
37+
xmltable('/ROWSET/ROW' passing x.item_data columns
38+
item_data xmltype path '*'
39+
,position for ordinality
40+
{:xml_to_columns:} ) ucd
41+
where data_id = :exp_guid
42+
) ucd
43+
)
44+
, act as (
45+
select
46+
ucd.*,
47+
{:duplicate_number:} dup_no
48+
from (
49+
select
50+
ucd.item_data
51+
,x.data_id data_id
52+
,{:item_no:} item_no
53+
{:columns:}
54+
from {:ut3_owner:}.ut_compound_data_tmp x,
55+
xmltable('/ROWSET/ROW' passing x.item_data columns
56+
item_data xmltype path '*'
57+
,position for ordinality
58+
{:xml_to_columns:} ) ucd
59+
where data_id = :act_guid
60+
) ucd
61+
)
62+
select
63+
a.item_data as act_item_data,
64+
a.data_id act_data_id,
65+
e.item_data as exp_item_data,
66+
e.data_id exp_data_id,
67+
{:item_no_select:} as item_no,
68+
nvl(e.dup_no,a.dup_no) dup_no
69+
from act a {:join_type:} exp e on ( {:join_condition:} )
70+
where {:where_condition:}]';
71+
2472
function get_columns_filter(
2573
a_exclude_xpath varchar2, a_include_xpath varchar2,
2674
a_table_alias varchar2 := 'ucd', a_column_alias varchar2 := 'item_data'
@@ -92,10 +140,10 @@ create or replace package body ut_compound_data_helper is
92140
return l_results;
93141
end;
94142

95-
procedure generate_not_equal_stmt(
96-
a_data_info ut_cursor_column, a_pk_table ut_varchar2_list,
97-
a_not_equal_stmt in out nocopy clob
98-
) is
143+
function generate_not_equal_stmt(
144+
a_data_info ut_cursor_column, a_pk_table ut_varchar2_list
145+
) return varchar2
146+
is
99147
l_pk_tab ut_varchar2_list := coalesce(a_pk_table,ut_varchar2_list());
100148
l_index integer;
101149
l_sql_stmt varchar2(32767);
@@ -110,19 +158,17 @@ create or replace package body ut_compound_data_helper is
110158
exit when l_index = l_pk_tab.count or (a_data_info.access_path = l_pk_tab(l_index));
111159
l_index := a_pk_table.next(l_index);
112160
end loop;
113-
end if;
114-
115-
if not(l_exists) then
116-
l_sql_stmt := l_sql_stmt || case when a_not_equal_stmt is null then null else ' or ' end
117-
||' (decode(a.'||a_data_info.transformed_name||','||' e.'||a_data_info.transformed_name||',1,0) = 0)';
118-
ut_utils.append_to_clob(a_not_equal_stmt,l_sql_stmt);
119161
end if;
162+
if not(l_exists) then
163+
l_sql_stmt := ' (decode(a.'||a_data_info.transformed_name||','||' e.'||a_data_info.transformed_name||',1,0) = 0)';
164+
end if;
165+
return l_sql_stmt;
120166
end;
121167

122-
procedure generate_join_by_stmt(
123-
a_data_info ut_cursor_column, a_pk_table ut_varchar2_list,
124-
a_join_by_stmt in out nocopy clob
125-
) is
168+
function generate_join_by_stmt(
169+
a_data_info ut_cursor_column, a_pk_table ut_varchar2_list
170+
) return varchar2
171+
is
126172
l_pk_tab ut_varchar2_list := coalesce(a_pk_table,ut_varchar2_list());
127173
l_index integer;
128174
l_sql_stmt varchar2(32767);
@@ -132,27 +178,24 @@ create or replace package body ut_compound_data_helper is
132178
loop
133179
if l_pk_tab(l_index) in (a_data_info.access_path, a_data_info.parent_name) then
134180
--When then table is nested and join is on whole table
135-
l_sql_stmt := case when a_join_by_stmt is null then null else ' and ' end;
136181
l_sql_stmt := l_sql_stmt ||' a.'||a_data_info.transformed_name||q'[ = ]'||' e.'||a_data_info.transformed_name;
137182
end if;
138183
exit when (a_data_info.access_path = l_pk_tab(l_index)) or l_index = l_pk_tab.count;
139184
l_index := l_pk_tab.next(l_index);
140-
end loop;
141-
ut_utils.append_to_clob(a_join_by_stmt,l_sql_stmt);
142-
end if;
185+
end loop;
186+
end if;
187+
return l_sql_stmt;
143188
end;
144189

145-
procedure generate_equal_sql(a_equal_stmt in out nocopy clob, a_col_name in varchar2) is
146-
l_sql_stmt varchar2(32767);
190+
function generate_equal_sql(a_col_name in varchar2) return varchar2 is
147191
begin
148-
l_sql_stmt := case when a_equal_stmt is null then null else ' and ' end ||' a.'||a_col_name||q'[ = ]'||' e.'||a_col_name;
149-
ut_utils.append_to_clob(a_equal_stmt,l_sql_stmt);
192+
return ' a.'||a_col_name||q'[ = ]'||' e.'||a_col_name;
150193
end;
151194

152-
procedure generate_partition_stmt(
153-
a_data_info ut_cursor_column, a_partition_stmt in out nocopy clob,
154-
a_pk_table in ut_varchar2_list, a_alias varchar2 := 'ucd.'
155-
) is
195+
function generate_partition_stmt(
196+
a_data_info ut_cursor_column, a_pk_table in ut_varchar2_list, a_alias varchar2 := 'ucd.'
197+
) return varchar2
198+
is
156199
l_index integer;
157200
l_sql_stmt varchar2(32767);
158201
begin
@@ -161,20 +204,20 @@ create or replace package body ut_compound_data_helper is
161204
loop
162205
if a_pk_table(l_index) in (a_data_info.access_path, a_data_info.parent_name) then
163206
--When then table is nested and join is on whole table
164-
l_sql_stmt := case when a_partition_stmt is null then null else ',' end;
165207
l_sql_stmt := l_sql_stmt ||a_alias||a_data_info.transformed_name;
166-
end if;
167-
208+
end if;
168209
exit when (a_data_info.access_path = a_pk_table(l_index)) or l_index = a_pk_table.count;
169210
l_index := a_pk_table.next(l_index);
170211
end loop;
171212
else
172-
l_sql_stmt := case when a_partition_stmt is null then null else ',' end ||a_alias||a_data_info.transformed_name;
213+
l_sql_stmt := a_alias||a_data_info.transformed_name;
173214
end if;
174-
ut_utils.append_to_clob(a_partition_stmt,l_sql_stmt);
215+
return l_sql_stmt;
175216
end;
176217

177-
procedure generate_select_stmt(a_data_info ut_cursor_column,a_sql_stmt in out nocopy clob, a_alias varchar2 := 'ucd.') is
218+
function generate_select_stmt(a_data_info ut_cursor_column, a_alias varchar2 := 'ucd.')
219+
return varchar2
220+
is
178221
l_alias varchar2(10) := a_alias;
179222
l_col_syntax varchar2(4000);
180223
l_ut_owner varchar2(250) := ut_utils.ut_owner;
@@ -183,12 +226,11 @@ create or replace package body ut_compound_data_helper is
183226
l_col_syntax := l_ut_owner ||'.ut_compound_data_helper.get_hash('||l_alias||a_data_info.transformed_name||'.getClobVal()) as '||a_data_info.transformed_name ;
184227
else
185228
l_col_syntax := l_alias||a_data_info.transformed_name||' as '|| a_data_info.transformed_name;
186-
end if;
187-
ut_utils.append_to_clob(a_sql_stmt,','||l_col_syntax);
229+
end if;
230+
return l_col_syntax;
188231
end;
189232

190-
procedure generate_xmltab_stmt(a_data_info ut_cursor_column, a_sql_stmt in out nocopy clob) is
191-
l_sql_stmt varchar2(32767);
233+
function generate_xmltab_stmt(a_data_info ut_cursor_column) return varchar2 is
192234
l_col_type varchar2(4000);
193235
begin
194236
if a_data_info.is_sql_diffable = 0 then
@@ -209,9 +251,7 @@ create or replace package body ut_compound_data_helper is
209251
else null
210252
end;
211253
end if;
212-
l_sql_stmt := case when a_sql_stmt is not null then ', ' end
213-
||a_data_info.transformed_name||' '||l_col_type||q'[ PATH ']'||a_data_info.access_path||q'[']';
214-
ut_utils.append_to_clob(a_sql_stmt, l_sql_stmt);
254+
return a_data_info.transformed_name||' '||l_col_type||q'[ PATH ']'||a_data_info.access_path||q'[']';
215255
end;
216256

217257
procedure gen_sql_pieces_out_of_cursor(
@@ -225,76 +265,70 @@ create or replace package body ut_compound_data_helper is
225265
a_not_equal_stmt out nocopy clob
226266
) is
227267
l_partition_tmp clob;
268+
l_xmltab_list ut_varchar2_list := ut_varchar2_list();
269+
l_select_list ut_varchar2_list := ut_varchar2_list();
270+
l_partition_list ut_varchar2_list := ut_varchar2_list();
271+
l_equal_list ut_varchar2_list := ut_varchar2_list();
272+
l_join_by_list ut_varchar2_list := ut_varchar2_list();
273+
l_not_equal_list ut_varchar2_list := ut_varchar2_list();
274+
275+
procedure add_element_to_list(a_list in out ut_varchar2_list, a_list_element in varchar2)
276+
is
277+
begin
278+
if a_list_element is not null then
279+
a_list.extend;
280+
a_list(a_list.last) := a_list_element;
281+
end if;
282+
end;
283+
228284
begin
229285
if a_data_info is not empty then
230286
for i in 1..a_data_info.count loop
231287
if a_data_info(i).has_nested_col = 0 then
232288
--Get XMLTABLE column list
233-
generate_xmltab_stmt(a_data_info(i), a_xml_stmt);
289+
add_element_to_list(l_xmltab_list,generate_xmltab_stmt(a_data_info(i)));
234290
--Get Select statment list of columns
235-
generate_select_stmt(a_data_info(i), a_select_stmt);
291+
add_element_to_list(l_select_list, generate_select_stmt(a_data_info(i)));
236292
--Get columns by which we partition
237-
generate_partition_stmt(a_data_info(i), l_partition_tmp, a_pk_table);
293+
add_element_to_list(l_partition_list,generate_partition_stmt(a_data_info(i), a_pk_table));
238294
--Get equal statement
239-
generate_equal_sql(a_equal_stmt, a_data_info(i).transformed_name);
295+
add_element_to_list(l_equal_list,generate_equal_sql(a_data_info(i).transformed_name));
240296
--Generate join by stmt
241-
generate_join_by_stmt(a_data_info(i), a_pk_table, a_join_by_stmt);
297+
add_element_to_list(l_join_by_list,generate_join_by_stmt(a_data_info(i), a_pk_table));
242298
--Generate not equal stmt
243-
generate_not_equal_stmt(a_data_info(i), a_pk_table, a_not_equal_stmt);
299+
add_element_to_list(l_not_equal_list,generate_not_equal_stmt(a_data_info(i), a_pk_table));
244300
end if;
245301
end loop;
246-
ut_utils.append_to_clob(a_partition_stmt,', row_number() over (partition by ');
247-
ut_utils.append_to_clob(a_partition_stmt, l_partition_tmp||' order by '||l_partition_tmp||' ) dup_no ');
302+
303+
a_xml_stmt := nullif(','||ut_utils.table_to_clob(l_xmltab_list, ' , '),',');
304+
a_select_stmt := nullif(','||ut_utils.table_to_clob(l_select_list, ' , '),',');
305+
l_partition_tmp := ut_utils.table_to_clob(l_partition_list, ' , ');
306+
ut_utils.append_to_clob(a_partition_stmt,' row_number() over (partition by '||l_partition_tmp||' order by '||l_partition_tmp||' ) ');
307+
a_equal_stmt := ut_utils.table_to_clob(l_equal_list, ' and ');
308+
a_join_by_stmt := ut_utils.table_to_clob(l_join_by_list, ' and ');
309+
a_not_equal_stmt := ut_utils.table_to_clob(l_not_equal_list, ' or ');
248310
else
249311
--Partition by piece when no data
250-
ut_utils.append_to_clob(a_partition_stmt,', 1 dup_no ');
312+
ut_utils.append_to_clob(a_partition_stmt,' 1 ');
251313
end if;
252314
end;
253315

254-
procedure get_act_and_exp_set(
255-
a_current_stmt in out nocopy clob, a_partition_stmt clob, a_select_stmt clob,
256-
a_xmltable_stmt clob, a_unordered boolean, a_type varchar2
257-
) is
258-
l_temp_string varchar2(32767);
259-
l_ut_owner varchar2(250) := ut_utils.ut_owner;
260-
begin
261-
ut_utils.append_to_clob(a_current_stmt, a_partition_stmt);
262-
263-
l_temp_string := 'from (select ucd.item_data ';
264-
ut_utils.append_to_clob(a_current_stmt,l_temp_string);
265-
ut_utils.append_to_clob(a_current_stmt, a_select_stmt);
266-
267-
l_temp_string := ',x.data_id, '
268-
|| case when not a_unordered then 'position + x.item_no ' else 'rownum ' end
269-
||'item_no from ' || l_ut_owner || '.ut_compound_data_tmp x,'
270-
||q'[ xmltable('/ROWSET/ROW' passing x.item_data columns ]' ;
271-
ut_utils.append_to_clob(a_current_stmt, l_temp_string);
272-
273-
ut_utils.append_to_clob(a_current_stmt, a_xmltable_stmt);
274-
ut_utils.append_to_clob(a_current_stmt, case when a_xmltable_stmt is null then '' else ',' end||q'[ item_data xmltype PATH '*']');
275-
if not a_unordered then
276-
ut_utils.append_to_clob(a_current_stmt, ', position for ordinality ');
277-
end if;
278-
ut_utils.append_to_clob(a_current_stmt, ' ) ucd where data_id = :'||a_type||'_guid ) ucd ) ');
279-
end;
280-
281-
282316
function gen_compare_sql(
283317
a_other ut_data_value_refcursor,
284318
a_join_by_list ut_varchar2_list,
285319
a_unordered boolean,
286320
a_inclusion_type boolean,
287321
a_is_negated boolean
288322
) return clob is
289-
l_compare_sql clob;
290-
l_temp_string varchar2(32767);
291-
323+
l_compare_sql clob;
292324
l_xmltable_stmt clob;
293325
l_select_stmt clob;
294326
l_partition_stmt clob;
295327
l_equal_stmt clob;
296328
l_join_on_stmt clob;
297329
l_not_equal_stmt clob;
330+
l_where_stmt clob;
331+
l_ut_owner varchar2(250) := ut_utils.ut_owner;
298332

299333
function get_join_type(a_inclusion_compare in boolean,a_negated in boolean) return varchar2 is
300334
begin
@@ -305,62 +339,65 @@ create or replace package body ut_compound_data_helper is
305339
else ' full outer join '
306340
end;
307341
end;
308-
342+
343+
function get_item_no(a_unordered boolean) return varchar2 is
344+
begin
345+
return
346+
case
347+
when not a_unordered then 'position + x.item_no '
348+
else 'rownum '
349+
end;
350+
end;
351+
309352
begin
310-
dbms_lob.createtemporary(l_compare_sql, true);
353+
dbms_lob.createtemporary(l_compare_sql, true);
354+
--Initiate a SQL template with placeholders
355+
ut_utils.append_to_clob(l_compare_sql, gc_compare_sql_template);
356+
--Generate a pieceso of dynamic SQL that will substitute placeholders
311357
gen_sql_pieces_out_of_cursor(
312358
a_other.cursor_details.cursor_columns_info, a_join_by_list,
313359
l_xmltable_stmt, l_select_stmt, l_partition_stmt, l_equal_stmt,
314360
l_join_on_stmt, l_not_equal_stmt
315361
);
316362

317-
l_temp_string := 'with exp as ( select ucd.* ';
318-
ut_utils.append_to_clob(l_compare_sql, l_temp_string);
319-
get_act_and_exp_set(l_compare_sql, l_partition_stmt, l_select_stmt, l_xmltable_stmt, a_unordered ,'exp');
363+
l_compare_sql := replace(l_compare_sql,'{:duplicate_number:}',l_partition_stmt);
364+
l_compare_sql := replace(l_compare_sql,'{:columns:}',l_select_stmt);
365+
l_compare_sql := replace(l_compare_sql,'{:item_no:}',get_item_no(a_unordered));
366+
l_compare_sql := replace(l_compare_sql,'{:ut3_owner:}',l_ut_owner);
367+
l_compare_sql := replace(l_compare_sql,'{:xml_to_columns:}',l_xmltable_stmt);
368+
l_compare_sql := replace(l_compare_sql,'{:item_no_select:}',case when a_unordered then 'rownum' else 'nvl(e.item_no,a.item_no)' end);
369+
l_compare_sql := replace(l_compare_sql,'{:join_type:}',get_join_type(a_inclusion_type,a_is_negated));
320370

321-
322-
l_temp_string :=',act as ( select ucd.* ';
323-
ut_utils.append_to_clob(l_compare_sql, l_temp_string);
324-
get_act_and_exp_set(l_compare_sql, l_partition_stmt, l_select_stmt, l_xmltable_stmt, a_unordered ,'act');
325-
326-
l_temp_string := ' select a.item_data as act_item_data, a.data_id act_data_id,'
327-
||'e.item_data as exp_item_data, e.data_id exp_data_id, '||
328-
case when a_unordered then 'rownum item_no' else 'nvl(e.item_no,a.item_no) item_no' end ||', nvl(e.dup_no,a.dup_no) dup_no '
329-
||'from act a '||get_join_type(a_inclusion_type,a_is_negated)||' exp e on ( ';
330-
ut_utils.append_to_clob(l_compare_sql,l_temp_string);
331-
332-
if a_unordered then
333-
ut_utils.append_to_clob(l_compare_sql,' e.dup_no = a.dup_no and ');
334-
end if;
335-
371+
--TODO : Refactor as equal and join is single stmt
336372
if (a_join_by_list.count > 0) then
337373
-- If key defined do the join or these and where on diffrences
338-
ut_utils.append_to_clob(l_compare_sql,l_join_on_stmt);
374+
l_compare_sql := replace(l_compare_sql,'{:join_condition:}',l_join_on_stmt);
339375
elsif a_unordered then
340376
-- If no key defined do the join on all columns
341-
ut_utils.append_to_clob(l_compare_sql,l_equal_stmt);
377+
l_compare_sql := replace(l_compare_sql,'{:join_condition:}',' e.dup_no = a.dup_no and '||l_equal_stmt);
342378
else
343-
ut_utils.append_to_clob(l_compare_sql, 'a.item_no = e.item_no ' );
379+
l_compare_sql := replace(l_compare_sql,'{:join_condition:}','a.item_no = e.item_no ');
344380
end if;
345381

346-
ut_utils.append_to_clob(l_compare_sql,' ) where ');
347-
348382
if l_not_equal_stmt is not null then
349383
if (a_join_by_list.count > 0 and not a_is_negated) or (not a_unordered) then
350-
ut_utils.append_to_clob(l_compare_sql,' ( '||l_not_equal_stmt||' ) or ');
384+
ut_utils.append_to_clob(l_where_stmt,' ( '||l_not_equal_stmt||' ) or ');
351385
end if;
352386
end if;
353387

354388
--If its inclusion we expect a actual set to fully match and have no extra elements over expected
355389
if a_inclusion_type then
356-
l_temp_string := case when a_is_negated then ' 1 = 1 ' else ' ( a.data_id is null ) ' end;
390+
ut_utils.append_to_clob(l_where_stmt,case when a_is_negated then ' 1 = 1 ' else ' ( a.data_id is null ) ' end);
357391
else
358-
l_temp_string := ' (a.data_id is null or e.data_id is null) ';
359-
end if;
360-
ut_utils.append_to_clob(l_compare_sql,l_temp_string);
392+
ut_utils.append_to_clob(l_where_stmt,' (a.data_id is null or e.data_id is null) ');
393+
end if;
394+
395+
l_compare_sql := replace(l_compare_sql,'{:where_condition:}',l_where_stmt);
396+
397+
--dbms_output.put_line(l_compare_sql);
361398
return l_compare_sql;
362399
end;
363-
400+
364401
function get_column_extract_path(a_cursor_info ut_cursor_column_tab) return ut_varchar2_list is
365402
l_column_list ut_varchar2_list := ut_varchar2_list();
366403
begin

0 commit comments

Comments
 (0)